Time Series MAANG dengan Metode ARIMA & Kombinasi Drift

Angga Fathan Rofiqy

14 November, 2023

Kode di Hide dalam default, untuk menampilkan kode, klik Code .

#                      -=( Install & Load Package Function )=-
install_load <- function (package1, ...)  {   

   # convert arguments to vector
   packages <- c(package1, ...)

   # start loop to determine if each package is installed
   for(package in packages){

       # if package is installed locally, load
       if(package %in% rownames(installed.packages()))
          do.call('library', list(package))

       # if package is not installed locally, download, then load
       else {
          install.packages(package)
          do.call("library", list(package))
       }
   } 
}

#Path Function
path <- function(){
  gsub  ( "\\\\",  "/",  readClipboard ()  )
}
#Copy path, Panggil function di console
#Copy r path, paste ke var yang diinginkan
#Export chart
export.chart <- "C:/Users/Fathan/Documents/Obsidian Vault/2. Kuliah/Smt 5/6. Metode Peramalan Deret Waktu/@Proj/STA1341-MPDW/Pertemuan 10/Chart"

1 Pendahuluan

Dataset yang saya gunakan merupakan koleksi data harga saham historis periode Juli 2018 hingga Juli 2023 dari tujuh raksasa teknologi paling berpengaruh di dunia: Microsoft, Apple, Amazon, Nvidia, Google, Netflix, dan Meta (sebelumnya dikenal sebagai Facebook). Dataset ini menjadi sumber daya berharga bagi analis keuangan, ilmuwan data, dan penggemar pasar saham yang ingin menganalisis dan memahami tren harga perusahaan-perusahaan terkemuka di industri ini.

Dataset ini memilki data :

  1. Open: yakni Harga saham pada awal periode perdagangan tertentu. Ini adalah harga saham pertama pada hari perdagangan tersebut.
  2. High: Harga tertinggi yang saham capai selama periode perdagangan tersebut. Ini mencerminkan harga tertinggi yang pembeli bersedia bayar selama hari tersebut.
  3. Low: Harga terendah yang saham capai selama periode perdagangan tersebut. Ini mencerminkan harga terendah yang penjual bersedia terima selama hari tersebut.
  4. Close: Harga saham pada akhir periode perdagangan tertentu. Ini adalah harga saham terakhir pada hari perdagangan tersebut.
  5. Adj Close (Adjusted Close): Harga penutup yang telah disesuaikan untuk memperhitungkan perubahan seperti pembagian saham atau dividen. Ini adalah harga penutup yang paling relevan untuk analisis jangka panjang, karena mencerminkan harga saham yang sebenarnya setelah penyesuaian.
  6. Volume: Volume perdagangan saham selama periode tertentu. Ini mencerminkan jumlah saham yang diperdagangkan selama hari perdagangan tersebut.

Kami akan menggunakan peubah Adj Close (Adjusted Close), Karena sesuai dengan penjelasan diatas, peubah Adj Close adalah yang paling sesuai untuk dianalisis dibandingkan peubah lainnya.

1.1 Tujuan

Tujuan dari praktikum ini adalah untuk menganalisis pola perkiraan pergerakan harga tujuh saham teknologi terkemuka dengan harapan dapat memberikan rekomendasi kepada pembaca mengenai saham mana yang sebaiknya dipertimbangkan untuk dibeli atau diinvestasikan secara signifikan di antara tujuh perusahaan teknologi besar tersebut.

1.2 Data Preparation

1.2.1 Import Data

install_load('rio')
raw.data <- import("https://raw.githubusercontent.com/Zen-Rofiqy/STA1341-MPDW/main/Data/New/%40MAANG%20Stock%20Prices.csv")

1.2.2 Data Checking

Cek Tipe data.

str(raw.data)
## 'data.frame':    10332 obs. of  8 variables:
##  $ Name     : chr  "AAPL" "AAPL" "AAPL" "AAPL" ...
##  $ Date     : IDate, format: "2018-01-02" "2018-01-03" ...
##  $ Open     : num  42.5 43.1 43.1 43.4 43.6 ...
##  $ High     : num  43.1 43.6 43.4 43.8 43.9 ...
##  $ Low      : num  42.3 43 43 43.3 43.5 ...
##  $ Close    : num  43.1 43.1 43.3 43.8 43.6 ...
##  $ Adj Close: num  40.7 40.7 40.9 41.4 41.2 ...
##  $ Volume   : int  102223600 118071600 89738400 94640000 82271200 86336000 95839600 74670800 101672400 118263600 ...

Semua data Karakter, harus diubah.

Cek Data kosong.

sum(is.na(raw.data))
## [1] 0

Tidak ada data kosong.

1.2.3 Penyesuaian Tipe Data

Semua tipe data masih berupa character. Harus diubah menjadi tipe data yang sesuai.

install_load('dplyr')
data <- raw.data %>%  
  mutate(
    Date = as.Date(raw.data[, 2], format = "%m/%d/%y"), #Mengubah menjadi Date 
    across(3:ncol(raw.data), as.numeric)                #Mengubah menjadi Numerik
  )
str(data)
## 'data.frame':    10332 obs. of  8 variables:
##  $ Name     : chr  "AAPL" "AAPL" "AAPL" "AAPL" ...
##  $ Date     : Date, format: "2018-01-02" "2018-01-03" ...
##  $ Open     : num  42.5 43.1 43.1 43.4 43.6 ...
##  $ High     : num  43.1 43.6 43.4 43.8 43.9 ...
##  $ Low      : num  42.3 43 43 43.3 43.5 ...
##  $ Close    : num  43.1 43.1 43.3 43.8 43.6 ...
##  $ Adj Close: num  40.7 40.7 40.9 41.4 41.2 ...
##  $ Volume   : num  1.02e+08 1.18e+08 8.97e+07 9.46e+07 8.23e+07 ...

1.2.4 Rechecking Data 

Cek kembali data kosong.

cat('Banyaknya Data Kosong', sum(is.na(data)))
## Banyaknya Data Kosong 0

1.2.5 Cek Periode Data

data2 <- data
install_load("lubridate")

dates <- as.Date(data$Date)

# Buat rentang waktu mulai dari tanggal pertama hingga tanggal terakhir dalam data
full_date_range <- seq(min(dates), max(dates), by = "days")

# Bandingkan rentang waktu dengan tanggal yang ada dalam data
missing_dates <- setdiff(full_date_range, dates) 

# Jika 'missing_dates' kosong, maka semua tanggal sudah ada dalam data
if (length(missing_dates) == 0) {
  cat("Semua tanggal ada dalam data.\n")
} else {
  cat("Tanggal yang tidak ada dalam data sebanyak", length(missing_dates),
      "\nAtau sebanyak", length(missing_dates) * 7, 
      "Data Hilang dari ke-7 perusahaan yang ada")
}
## Tanggal yang tidak ada dalam data sebanyak 663 
## Atau sebanyak 4641 Data Hilang dari ke-7 perusahaan yang ada

Inputasi Data

install_load('purrr')
# Fungsi untuk mengisi data yang hilang
fill_missing_data <- function(name) {
  data_filtered <- data2 %>%
    filter(Name == name)
  
  full_date_range <- seq(min(data2$Date), max(data2$Date), by = "days")
  data_frame_template <- data.frame(Date = full_date_range)
  
  # Menambahkan kolom "Name" sesuai dengan perusahaan yang diproses
  data_frame_template$Name <- name
  
  data_filled <- merge(data_frame_template, data_filtered, 
                       by = c("Date", "Name"), all.x = TRUE)
  return(data_filled)
}

# Menggunakan purrr::map untuk memproses setiap nama perusahaan
filled_data_list <- map(unique(data2$Name), fill_missing_data)

# Gabungkan data-data yang telah diisi menjadi satu data frame
final_data <- data.frame()
final_data <- do.call(rbind, filled_data_list)

# Urutkan data berdasarkan "Name" terlebih dahulu, kemudian "Date"
final_data <- final_data %>%
  dplyr::select(1, 2, 7) %>%
  arrange(Name, Date)

#Input Data Hilang
install_load('imputeTS')

data <- na_interpolation(final_data$`Adj Close`)

install_load('ggplot2')
chart <- ggplot_na_imputations(final_data$`Adj Close`, data)
chart

#Export Chart
ggsave("00_Imputasi Data.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 23)

data <- final_data %>% select(Name, Date) %>% 
  mutate(`Adj Close` = data)

Data sudah di imputasi.

Cek ukuran data

cat("Ukuran data awal adalah", nrow(data2),
    "\nBanyaknya peride data yang hilang", length(missing_dates),
    "(PerSaham.", length(missing_dates) * 7, "Jika semua)",
    "\nUkuran data yang baru seharusnya =", 
    nrow(data2) + length(missing_dates) * 7,
    "\nIni sudah sesuai dengan Ukuran data input yakni =", nrow(data))
## Ukuran data awal adalah 10332 
## Banyaknya peride data yang hilang 663 (PerSaham. 4641 Jika semua) 
## Ukuran data yang baru seharusnya = 14973 
## Ini sudah sesuai dengan Ukuran data input yakni = 14973

Data sudah benar, siap untuk dianalisis.

1.2.6 Data Cleaned

install_load('DT')
datatable(data, filter = 'top', 
          options = list(pageLength = 5))

2 Eksplorasi Data

Referensi : Warna1, Warna2

col.aapl <- c("#6F8086", "#B4CBD2"); col.amzn <- c("#EDB64E", "#F6DEB3") 
col.goog <- c("#919C49", "#CCD775"); col.meta <- c("#4B75BA", "#9BC6EA") 
col.msft <- c("#4EC2C1", "#B8E0DC"); col.nflx <- c("#D02A49", "#F3AEAA") 
col.nvda <- c("#36B450", "#ACD694")

cols <- c("AAPL"=col.aapl[1], "test_aapl"=col.aapl[2],
          "AMZN"=col.amzn[1], "test_amzn"=col.amzn[2],
          "GOOG"=col.goog[1], "test_goog"=col.goog[2],
          "META"=col.meta[1], "test_meta"=col.meta[2],
          "MSFT"=col.msft[1], "test_msft"=col.msft[2],
          "NFLX"=col.nflx[1], "test_nflx"=col.nflx[2],
          "NVDA"=col.nvda[1], "test_nvda"=col.nvda[2] )

2.1 Plot Time Series

install_load('ggplot2','extrafont')
# font_import(); loadfonts() #Run ini sekali aja
theme.ts <- list(
  theme(legend.position = "none",
        axis.text.x = element_text(hjust = 1, 
                                   margin = margin(b = 10, t=20)),
        axis.text.y = element_text(vjust = 0.5, face = "bold", 
                                   margin = margin(l = 20, r = 20)),
        plot.title = element_text(hjust = 0.5, face = "bold"),
        text = element_text(size = 30),
        plot.subtitle = element_text(hjust = 0.5),
        panel.background = element_rect(fill = 'transparent'),
        plot.background = element_rect(fill='transparent', color=NA),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        axis.line = element_line(linewidth = 1, colour = "black"))
        )
theme.ts1 <- list(
  theme(legend.position = "none",
        axis.text.x = element_text(hjust = 1, 
                                   margin = margin(b = 10, t=20)),
        axis.text.y = element_text(vjust = 0.5, face = "bold", 
                                   margin = margin(l = 50, r = 20)),
        plot.title = element_text(hjust = 0.5, face = "bold"),
        text = element_text(size = 30),
        plot.subtitle = element_text(hjust = 0.5),
        panel.background = element_rect(fill = 'transparent'),
        plot.background = element_rect(fill='transparent', color=NA),
        panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        axis.line = element_line(linewidth = 1, colour = "black"))
        )

2.1.1 MAANG

Melihat keseluruhan Time Series data saham.

install_load('viridis','ggrepel')
#Plot
chart <-
ggplot(data, aes(x=Date, y=`Adj Close`, color=Name, alpha=Name)) + #Data
  geom_line(aes(color=Name), linewidth=1.5) + #Timeseries
  #Color
  scale_color_manual(values = c(AAPL=col.aapl[1], AMZN=col.amzn[1],
                                GOOG=col.goog[1], META=col.meta[1],
                                MSFT=col.msft[1], NFLX=col.nflx[1], 
                                NVDA=col.nvda[1]) ) +
  scale_alpha_manual(values = c("NVDA" = 1, "NFLX" = .25, "MSFT" = .25, 
                                "META" = .25, "AAPL" = .25, "GOOG" = 1, 
                                "AMZN" = .25)) +
  theme.ts + #THeme
  labs(x = "\nPeriode (Tahun)", y='Harga Saham (USD)',
       title = "Time Series MAANG",
       subtitle = "Seperti apa sih pola deret waktu saham MAANG?\n") +
  # Label / legend
  geom_text_repel(
    data=data[data$Date == max(data$Date),], #Posisi di ujung data
    aes(color = Name, label = Name), #Warna garis & label saham
    size = 8, #Ukuran text
    nudge_x = 80, #Posisi Text (kanan 50)
    hjust = 0, #Ujung
    segment.size = 1,               #Ukuran garis
    segment.alpha = .75,             #transparasi garis
    segment.linetype = "dotted",    #Time garis
    box.padding = .4, #Biar label saham nggak dempetan
    segment.curvature = -0.1, #biar garis mulus
    segment.ncp = 8, 
    segment.angle = 60 
  ) +
  #Axis
    coord_cartesian(clip = "off"
  ) +
    scale_x_date( #Sumbu x
    date_breaks = "1 year",  # Menampilkan label setiap tahun
    date_labels = "%Y",  # Format label tahun
    limits = c(as.Date(min(data$Date)), 
               as.Date(max(data$Date)) + 120)
    #Tampilin lebih dari 20023-07-28 agar label saham bisa masuk
  ) +
    scale_y_continuous( #Sumbu y
    labels = scales::dollar_format(prefix = "$") #tambahin dolar
  ) +
    annotate( #Buat nandain batas data
    "text", x = as.Date(max(data$Date)), y = 50, 
    label = max(data$Date), size=6
  ) +
  geom_vline( #Buat garis batas data
    xintercept = as.numeric(as.Date( max(data$Date) )), 
             linetype = "dotted", color = "red")
chart

#Export Chart
ggsave("01_Time Series MAANG.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 25)

Data saham berakhir pada tanggal 28 juli dengan harga saham tertinggi yakni NVIDIA dan daham terendah yakni Google. Jika dilihat dari tahun 2019-2022, semua saham cenderung memiliki pola trend naik. Lalu dari 2021-2023 polanya cenderung trend turun. Untuk tugas praktikum kali ini, kami ingin membandingkan pengambilan rentang tahun mana yang lebih baik forcast/peramalannya. Pada Sesi ini hanya akan menggunakan rentang tahun 2022-2023 dengan tren cenderung turun.

data2 <- data %>%
  filter(Date >= as.Date("2021-01-01")) 
install_load('viridis','ggrepel')
#Plot
chart <-
ggplot(data2, aes(x=Date, y=`Adj Close`, color=Name, alpha=Name)) + #Data
  geom_line(aes(color=Name), linewidth=1.5) + #Timeseries
  #Color
  scale_color_manual(values = c(AAPL=col.aapl[1], AMZN=col.amzn[1],
                                GOOG=col.goog[1], META=col.meta[1],
                                MSFT=col.msft[1], NFLX=col.nflx[1], 
                                NVDA=col.nvda[1]) ) +
  scale_alpha_manual(values = c("NVDA" = 1, "NFLX" = .25, "MSFT" = .25, 
                                "META" = .25, "AAPL" = .25, "GOOG" = 1, 
                                "AMZN" = .25)) +
  theme.ts + #THeme
  labs(x = "\nPeriode (Tahun)", y='Harga Saham (USD)',
       title = "Time Series MAANG",
       subtitle = "Seperti apa sih pola deret waktu saham MAANG pada 2022-2023?\n") +
  # Label / legend
  geom_text_repel(
    data=data2[data2$Date == max(data2$Date),], #Posisi di ujung data
    aes(color = Name, label = Name), #Warna garis & label saham
    size = 8, #Ukuran text
    nudge_x = 20, #Posisi Text (kanan 50)
    hjust = 0, #Ujung
    segment.size = 1,               #Ukuran garis
    segment.alpha = .75,             #transparasi garis
    segment.linetype = "dotted",    #Time garis
    box.padding = .4, #Biar label saham nggak dempetan
    segment.curvature = -0.1, #biar garis mulus
    segment.ncp = 8, 
    segment.angle = 60 
  ) +
  #Axis
    coord_cartesian(clip = "off"
  ) +
    scale_x_date( #Sumbu x
    date_breaks = "1 year",  # Menampilkan label setiap tahun
    date_labels = "%Y",  # Format label tahun
    limits = c(as.Date(min(data2$Date)), 
               as.Date(max(data2$Date)) + 120)
    #Tampilin lebih dari 20023-07-28 agar label saham bisa masuk
  ) +
    scale_y_continuous( #Sumbu y
    labels = scales::dollar_format(prefix = "$") #tambahin dolar
  ) +
    annotate( #Buat nandain batas data
    "text", x = as.Date(max(data2$Date)), y = 50, 
    label = max(data2$Date), size=6
  ) +
  geom_vline( #Buat garis batas data
    xintercept = as.numeric(as.Date( max(data2$Date) )), 
             linetype = "dotted", color = "red")
chart

#Export Chart
ggsave("01_Time Series MAANG_2022-2023.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 23)

2.1.2 AAPL

aapl <- data2 %>%
  filter(Name == "AAPL")  # Filter data saham Amazon tahun 2022 ke atas

rownames(aapl) <- NULL
str(aapl)
## 'data.frame':    1044 obs. of  3 variables:
##  $ Name     : chr  "AAPL" "AAPL" "AAPL" "AAPL" ...
##  $ Date     : Date, format: "2021-01-01" "2021-01-02" ...
##  $ Adj Close: num  130 129 128 127 129 ...
datatable(aapl, filter = 'top', 
          options = list(pageLength = 5))

Mengubah Ajd Close Menjadi Time series.

aapl.ts <- ts(aapl[,3])

Ringkasan Data Ajd CLose.

summary(aapl.ts)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   114.5   139.2   150.0   153.0   169.3   195.9
min_value <- min(aapl$`Adj Close`)
min_date <- aapl$Date[which.min(aapl$`Adj Close`)]
percentage <- (which.min(aapl$`Adj Close`) / nrow(aapl)) * 100

chart <-
ggplot(aapl, aes(x=Date, y=`Adj Close`)) + 
  geom_line(aes(color=Name), linewidth=2) +
  scale_color_manual(values = col.aapl[1]) +
  labs(x = "\nPeriode (Tahun)", y='Saham Harga penutup',
       title = "Time Series Saham Apple",
       subtitle = "Seperti apa sih pola deret waktu saham Apple?\n") +
  theme(legend.position = "none") +
  theme.ts1 + 
  geom_vline(xintercept = as.numeric(min_date), 
             linetype = "dotted", color = "grey30", linewidth = 1.5) +
  geom_text(aes(x = min_date-1*40, y = max(`Adj Close`)*80/100, label = 
                  paste0("Titik Terendah\n","(",round(percentage, 2), "%)",
                         "   ",min_date)), 
            vjust = -1.5, hjust = 0, size = 7, color = "grey30") 
chart

#Export Chart
ggsave("03_Time Series Apple.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 20)

Berdasarkan grafik deret waktu yang disajikan, terlihat bahwa sekitar \(64.47\%\) dari data menunjukkan kecenderungan musiman yang menurun secara multipel. Kemudian, pada tanggal 2023-01-05, terjadi perubahan pola menjadi tren naik.

Pembagian Data Training Dan Test.

#membagi 80% data latih (training) dan 20% data uji (testing)
train_aapl <- aapl[1: round(nrow(aapl) *80/100),]
test_aapl <- aapl[round(nrow(aapl) *80/100 +1): nrow(aapl),]
train_aapl.ts <- ts(train_aapl[,3])
test_aapl.ts <- ts(train_aapl[,3])
chart <-
ggplot() + 
  geom_line(data = train_aapl, linewidth=2,
            aes(x = Date, y = `Adj Close`, col = "train_aapl")) +
  geom_line(data = test_aapl, linewidth=1.5,
            aes(x = Date, y = `Adj Close`, col = "test_aapl")) +
  geom_point(data = tail(train_aapl, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 25, shape = 21, color = "black", fill="grey30") +
  labs(x = "\nPeriode (Tahun)", y='Saham Harga penutup',
       title = "Time Series Saham Apple",
       subtitle = "Pembagian Data Training dan Test\n") +
  theme(legend.position = "none") +
            scale_colour_manual(values = c("train_aapl"=col.aapl[1], 
                                 "test_aapl"=col.aapl[2])) + 
  theme.ts1
chart

#Export Chart
ggsave("03_TS_AAPL_train-test.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 20)

Berdasarkan plot data deret waktu pada data latih (\(80\%\) dari data asli), terlihat bahwa data menunjukkan tren turun musiman multiplikatif. Ini mengisyaratkan bahwa data latih tidak memenuhi kriteria stasioneritas dalam rataan maupun ragam. Di sisi lain, dalam plot data uji (\(20\%\) dari data asli), terlihat adanya tren naik dan kurangnya nilai tengah yang stabil. Ini juga menunjukkan bahwa data uji tidak stasioner dalam rataan.

2.1.3 AMZN

amzn <- data2 %>%
  filter(Name == "AMZN")  # Filter data saham Amazon tahun 2022 ke atas

rownames(amzn) <- NULL
str(amzn)
## 'data.frame':    1044 obs. of  3 variables:
##  $ Name     : chr  "AMZN" "AMZN" "AMZN" "AMZN" ...
##  $ Date     : Date, format: "2021-01-01" "2021-01-02" ...
##  $ Adj Close: num  162 161 160 159 161 ...
datatable(amzn, filter = 'top', 
          options = list(pageLength = 5))

Mengubah Ajd Close Menjadi Time series.

amzn.ts <- ts(amzn[,3])

Ringkasan Data Ajd CLose.

summary(amzn.ts)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   81.82  113.78  138.49  137.73  163.65  186.57
min_value <- min(amzn$`Adj Close`)
min_date <- amzn$Date[which.min(amzn$`Adj Close`)]
percentage <- (which.min(amzn$`Adj Close`) / nrow(amzn)) * 100

chart <-
ggplot(amzn, aes(x=Date, y=`Adj Close`)) + 
  geom_line(aes(color=Name), linewidth=2) +
  scale_color_manual(values = col.amzn[1]) +
  labs(x = "\nPeriode (Tahun)", y='Saham Harga penutup',
       title = "Time Series Saham Amazon",
       subtitle = "Seperti apa sih pola deret waktu saham Amazon?\n") +
  theme(legend.position = "none") +
  theme.ts1 + 
  geom_vline(xintercept = as.numeric(min_date), 
             linetype = "dotted", color = "grey30", linewidth = 1.5) +
  geom_text(aes(x = min_date-1*40, y = max(`Adj Close`)*80/100, label = 
                  paste0("Titik Terendah\n","(",round(percentage, 2), "%)",
                         "   ",min_date)), 
            vjust = -1.5, hjust = 0, size = 7, color = "grey30") 
chart

#Export Chart
ggsave("02_Time Series Amazon.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 20)

Dari grafik deret waktu yang ditampilkan, terlihat bahwa sekitar \(63.2\%\) dari data menunjukkan kecenderungan musiman yang menurun secara aditif. Selanjutnya, pada tanggal 2022-12-28, terjadi perubahan pola menjadi tren naik.

Pembagian Data Training Dan Test.

#membagi 80% data latih (training) dan 20% data uji (testing)
train_amzn <- amzn[1: round(nrow(amzn) *82/100),]
test_amzn <- amzn[round(nrow(amzn) *82/100 +1): nrow(amzn),]
train_amzn.ts <- ts(train_amzn[,3])
test_amzn.ts <- ts(test_amzn[,3])
chart <-
ggplot() + 
  geom_line(data = train_amzn, linewidth=2,
            aes(x = Date, y = `Adj Close`, col = "train_amzn")) +
  geom_line(data = test_amzn, linewidth=1.5,
            aes(x = Date, y = `Adj Close`, col = "test_amzn")) +
  geom_point(data = tail(train_amzn, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 25, shape = 21, color = "black", fill="grey30") +
  labs(x = "\nPeriode (Tahun)", y='Saham Harga penutup',
       title = "Time Series Saham Amazon",
       subtitle = "Pembagian Data Training dan Test\n") +
  theme(legend.position = "none") +
          scale_colour_manual(values = c("train_amzn"=col.amzn[1], 
                                 "test_amzn"=col.amzn[2])) + 
  theme.ts1
chart

#Export Chart
ggsave("02_TS_Amazon_train-test.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 20)

Dari plot deret waktu data latih (\(80\%\) dari data asli), terlihat bahwa data menunjukkan tren turun musiman secara aditif. Ini menunjukkan ketidakstabilan dalam nilai tengah dan variabilitas data latih, yang tidak memenuhi kriteria stasioneritas dalam rataan maupun ragam. Di sisi lain, dalam plot data uji (\(20\%\) dari data asli), terlihat adanya tren naik dan absennya nilai tengah yang stabil. Hal ini juga mengindikasikan bahwa data uji tidak memenuhi kriteria stasioneritas dalam rataan.

2.1.4 GOOG

goog <- data2 %>%
  filter(Name == "GOOG")  # Filter data saham Amazon tahun 2022 ke atas

rownames(goog) <- NULL
str(goog)
## 'data.frame':    1044 obs. of  3 variables:
##  $ Name     : chr  "GOOG" "GOOG" "GOOG" "GOOG" ...
##  $ Date     : Date, format: "2021-01-01" "2021-01-02" ...
##  $ Adj Close: num  87.3 87 86.7 86.4 87 ...
datatable(goog, filter = 'top', 
          options = list(pageLength = 5))

Mengubah Ajd Close Menjadi Time series.

goog.ts <- ts(goog[,3])

Ringkasan Data Ajd CLose.

summary(goog.ts)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   83.49  104.45  119.88  119.17  134.69  150.71
min_value <- min(goog$`Adj Close`)
min_date <- goog$Date[which.min(goog$`Adj Close`)]
percentage <- (which.min(goog$`Adj Close`) / nrow(goog)) * 100

chart <-
ggplot(goog, aes(x=Date, y=`Adj Close`)) + 
  geom_line(aes(color=Name), linewidth=2) +
  scale_color_manual(values = col.goog[1]) +
  labs(x = "\nPeriode (Tahun)", y='Saham Harga penutup',
       title = "Time Series Saham Google",
       subtitle = "Seperti apa sih pola deret waktu saham Google?\n") +
  theme(legend.position = "none") +
  theme.ts1 + 
  geom_vline(xintercept = as.numeric(min_date), 
             linetype = "dotted", color = "grey30", linewidth = 1.5) +
  geom_text(aes(x = min_date-1*40, y = max(`Adj Close`)*80/100, label = 
                  paste0("Titik Terendah\n","(",round(percentage, 2), "%)",
                         "   ",min_date)), 
            vjust = -1.5, hjust = 0, size = 7, color = "grey30") 
chart

#Export Chart
ggsave("04_Time Series Google.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 20)

Melalui visualisasi grafik deret waktu yang disuguhkan, tampaknya sekitar \(53.81\%\) data menggambarkan tren menurun. Lalu, pada tanggal 2021-11-03, terjadi perubahan pola menjadi tren naik.

Pembagian Data Training Dan Test.

#membagi 80% data latih (training) dan 20% data uji (testing)
train_goog <- goog[1: round(nrow(goog) *86/100),]
test_goog <- goog[round(nrow(goog) *86/100 +1): nrow(goog),]
train_goog.ts <- ts(train_goog[,3])
test_goog.ts <- ts(test_goog[,3])
chart <-
ggplot() + 
  geom_line(data = train_goog, linewidth=2,
            aes(x = Date, y = `Adj Close`, col = "train_goog")) +
  geom_line(data = test_goog, linewidth=1.5,
            aes(x = Date, y = `Adj Close`, col = "test_goog")) +
  geom_point(data = tail(train_goog, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 25, shape = 21, color = "black", fill="grey30") +
  labs(x = "\nPeriode (Tahun)", y='Saham Harga penutup',
       title = "Time Series Saham Google",
       subtitle = "Pembagian Data Training dan Test\n") +
  theme(legend.position = "none") +
        scale_colour_manual(values = c("train_goog"=col.goog[1], 
                                 "test_goog"=col.goog[2])) + 
  theme.ts1
chart

#Export Chart
ggsave("04_TS_GOOG_train-test.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 20)

Melalui plot deret waktu dari data latih (\(80\%\) dari data asli), terlihat bahwa data menggambarkan tren menurun yang cukup jelas. Hal ini menunjukkan bahwa data latih tidak menunjukkan sifat stasioneritas dalam rataan. Sementara itu, dalam plot data uji (\(20\%\) dari data asli), tampaknya terdapat tren naik yang signifikan, dan tidak ada nilai tengah yang tetap. Ini juga mengindikasikan bahwa data uji tidak memenuhi kriteria stasioneritas dalam rataan.

2.1.5 META

meta <- data2 %>%
  filter(Name == "META")  # Filter data saham Amazon tahun 2022 ke atas

rownames(meta) <- NULL
str(meta)
## 'data.frame':    1044 obs. of  3 variables:
##  $ Name     : chr  "META" "META" "META" "META" ...
##  $ Date     : Date, format: "2021-01-01" "2021-01-02" ...
##  $ Adj Close: num  272 271 270 269 271 ...
datatable(meta, filter = 'top', 
          options = list(pageLength = 5))

Mengubah Ajd Close Menjadi Time series.

meta.ts <- ts(meta[,3])

Ringkasan Data Ajd CLose.

summary(meta.ts)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   88.91  180.90  265.80  250.16  317.94  382.18
min_value <- min(meta$`Adj Close`)
min_date <- meta$Date[which.min(meta$`Adj Close`)]
percentage <- (which.min(meta$`Adj Close`) / nrow(meta)) * 100

chart <-
ggplot(meta, aes(x=Date, y=`Adj Close`)) + 
  geom_line(aes(color=Name), linewidth=2) +
  scale_color_manual(values = col.meta[1]) +
  labs(x = "\nPeriode (Tahun)", y='Saham Harga penutup',
       title = "Time Series Saham META",
       subtitle = "Seperti apa sih pola deret waktu saham META?\n") +
  theme(legend.position = "none") +
  theme.ts1 + 
  geom_vline(xintercept = as.numeric(min_date), 
             linetype = "dotted", color = "grey30", linewidth = 1.5) +
  geom_text(aes(x = min_date-1*40, y = max(`Adj Close`)*80/100, label = 
                  paste0("Titik Terendah\n","(",round(percentage, 2), "%)",
                         "   ",min_date)), 
            vjust = -1.5, hjust = 0, size = 7, color = "grey30") 
chart

#Export Chart
ggsave("05_Time Series META.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 20)

Melalui grafik deret waktu yang disajikan, terlihat bahwa sekitar \(53.81\%\) dari data menunjukkan kecenderungan penurunan tren. Lalu, pada tanggal 2022-11-03, terjadi perubahan pola menjadi tren naik. Pola perubahan ini, beserta tanggal dan persentasenya, serupa dengan yang terlihat dalam pergerakan saham Google.

Pembagian Data Training Dan Test2

#membagi 80% data latih (training) dan 20% data uji (testing)
train_meta <- meta[1: round(nrow(meta) *76/100),]
test_meta <- meta[round(nrow(meta) *76/100 +1): nrow(meta),]
train_meta.ts <- ts(train_meta[,3])
test_meta.ts <- ts(test_meta[,3])
chart <-
ggplot() + 
  geom_line(data = train_meta, linewidth=2,
            aes(x = Date, y = `Adj Close`, col = "train_meta")) +
  geom_line(data = test_meta, linewidth=1.5,
            aes(x = Date, y = `Adj Close`, col = "test_meta")) +
  geom_point(data = tail(train_meta, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 25, shape = 21, color = "black", fill="grey30") +
  labs(x = "\nPeriode (Tahun)", y='Saham Harga penutup',
       title = "Time Series Saham META",
       subtitle = "Pembagian Data Training dan Test\n") +
  theme(legend.position = "none") +
      scale_colour_manual(values = c("train_meta"=col.meta[1], 
                                 "test_meta"=col.meta[2])) + 
  theme.ts1
chart

#Export Chart
ggsave("05_TS_META_train-test.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 20)

Berdasarkan grafik deret waktu data latih (\(80\%\) dari data asli), tampak bahwa data menunjukkan kombinasi tren turun dan tren naik. Hal ini menunjukkan bahwa data latih tidak memenuhi syarat stasioneritas dalam rataan maupun ragam. Sementara itu, pada plot data uji (\(20\%\) dari data asli), terlihat tren naik yang signifikan dan ketiadaan nilai tengah yang konsisten. Ini juga menandakan bahwa data uji tidak memenuhi syarat stasioneritas dalam rataan.

2.1.6 MSFT

msft <- data2 %>%
  filter(Name == "MSFT")  # Filter data saham Amazon tahun 2022 ke atas

rownames(msft) <- NULL
str(msft)
## 'data.frame':    1044 obs. of  3 variables:
##  $ Name     : chr  "MSFT" "MSFT" "MSFT" "MSFT" ...
##  $ Date     : Date, format: "2021-01-01" "2021-01-02" ...
##  $ Adj Close: num  216 215 214 212 213 ...
datatable(msft, filter = 'top', 
          options = list(pageLength = 5))

Mengubah Ajd Close Menjadi Time series.

msft.ts <- ts(msft[,3])

Ringkasan Data Ajd CLose.

summary(msft.ts)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   207.1   246.5   276.5   278.7   308.9   369.7
min_value <- min(msft$`Adj Close`)
min_date <- msft$Date[which.min(msft$`Adj Close`)]
percentage <- (which.min(msft$`Adj Close`) / nrow(msft)) * 100

chart <-
ggplot(msft, aes(x=Date, y=`Adj Close`)) + 
  geom_line(aes(color=Name), linewidth=2) +
  scale_color_manual(values = col.msft[1]) +
  labs(x = "\nPeriode (Tahun)", y='Saham Harga penutup',
       title = "Time Series Saham Microsoft",
       subtitle = "Seperti apa sih pola deret waktu saham Microsoft?\n") +
  theme(legend.position = "none") +
  theme.ts1 + 
  geom_vline(xintercept = as.numeric(min_date), 
             linetype = "dotted", color = "grey30", linewidth = 1.5) +
  geom_text(aes(x = min_date-1*40, y = max(`Adj Close`)*80/100, label = 
                  paste0("Titik Terendah\n","(",round(percentage, 2), "%)",
                         "   ",min_date)), 
            vjust = -1.5, hjust = 0, size = 7, color = "grey30") 
chart

#Export Chart
ggsave("06_Time Series Microsoft.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 20)

Dalam gambaran grafik deret waktu yang diberikan, sekitar $53.81%$ data menunjukkan adanya tren penurunan yang tampak. Lalu, pada tanggal 2022-11-03, terjadi perubahan pola menjadi tren kenaikan. Karakteristik tanggal dan persentase perubahan pola ini serupa dengan yang dapat ditemukan pada pergerakan saham Google dan META.

Pembagian Data Training Dan Test.

#membagi 80% data latih (training) dan 20% data uji (testing)
train_msft <- msft[1: round(nrow(msft) *80/100),]
test_msft <- msft[round(nrow(msft) *80/100 +1): nrow(msft),]
train_msft.ts <- ts(train_msft[,3])
test_msft.ts <- ts(test_msft[,3])
chart <-
ggplot() + 
  geom_line(data = train_msft, linewidth=2,
            aes(x = Date, y = `Adj Close`, col = "train_msft")) +
  geom_line(data = test_msft, linewidth=1.5,
            aes(x = Date, y = `Adj Close`, col = "test_msft")) +
  geom_point(data = tail(train_msft, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 25, shape = 21, color = "black", fill="grey30") +
  labs(x = "\nPeriode (Tahun)", y='Saham Harga penutup',
       title = "Time Series Saham Microsoft",
       subtitle = "Pembagian Data Training dan Test\n") +
  theme(legend.position = "none") +
    scale_colour_manual(values = c("train_msft"=col.msft[1], 
                                 "test_msft"=col.msft[2])) + 
  theme.ts1
chart

#Export Chart
ggsave("06_TS_MSFT_train-test.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 20)

Dari visualisasi grafik deret waktu pada data latih (\(80\%\) dari data asli), terlihat bahwa data menunjukkan adanya tren turun dan tren naik. Hal ini menunjukkan bahwa data latih tidak memenuhi kriteria stasioneritas dalam rataan maupun ragam. Sementara itu, dalam plot data uji (\(20\%\) dari data asli), tampaknya terdapat tren kenaikan yang signifikan dan ketiadaan nilai tengah yang stabil. Hal ini juga menandakan bahwa data uji tidak memenuhi persyaratan stasioneritas dalam rataan.

2.1.7 NFLX

nflx <- data2 %>%
  filter(Name == "NFLX")  # Filter data saham Amazon tahun 2022 ke atas

rownames(nflx) <- NULL
str(nflx)
## 'data.frame':    1044 obs. of  3 variables:
##  $ Name     : chr  "NFLX" "NFLX" "NFLX" "NFLX" ...
##  $ Date     : Date, format: "2021-01-01" "2021-01-02" ...
##  $ Adj Close: num  536 532 527 523 521 ...
datatable(nflx, filter = 'top', 
          options = list(pageLength = 5))

Mengubah Ajd Close Menjadi Time series.

nflx.ts <- ts(nflx[,3])

Ringkasan Data Ajd CLose.

summary(nflx.ts)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   166.4   309.6   400.7   408.5   519.0   691.7
min_value <- min(nflx$`Adj Close`)
min_date <- nflx$Date[which.min(nflx$`Adj Close`)]
percentage <- (which.min(nflx$`Adj Close`) / nrow(nflx)) * 100

chart <-
ggplot(nflx, aes(x=Date, y=`Adj Close`)) + 
  geom_line(aes(color=Name), linewidth=2) +
  scale_color_manual(values = col.nflx[1]) +
  labs(x = "\nPeriode (Tahun)", y='Saham Harga penutup',
       title = "Time Series Saham Netflix",
       subtitle = "Seperti apa sih pola deret waktu saham Netflix?\n") +
  theme(legend.position = "none") +
  theme.ts1 + 
  geom_vline(xintercept = as.numeric(min_date), 
             linetype = "dotted", color = "grey30", linewidth = 1.5) +
  geom_text(aes(x = min_date-1*40, y = max(`Adj Close`)*80/100, label = 
                  paste0("Titik Terendah\n","(",round(percentage, 2), "%)",
                         "   ",min_date)), 
            vjust = -1.5, hjust = 0, size = 7, color = "grey30") 
chart

#Export Chart
ggsave("07_Time Series Netflix.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 20)

Berbeda dari yang lain, berdasarkan grafik deret waktu yang disajikan, terlihat bahwa sekitar \(22.84\%\) dari data menunjukkan kecenderungan tren menurun. Kemudian, pada tanggal 2022-05-11, terjadi perubahan pola menjadi tren naik.

Pembagian Data Training Dan Test.

#membagi 80% data latih (training) dan 20% data uji (testing)
train_nflx <- nflx[1: round(nrow(nflx) *80/100),]
test_nflx <- nflx[round(nrow(nflx) *80/100  +1): nrow(nflx),]
train_nflx.ts <- ts(train_nflx[,3])
test_nflx.ts <- ts(test_nflx[,3])
chart <-
ggplot() + 
  geom_line(data = train_nflx, linewidth=2,
            aes(x = Date, y = `Adj Close`, col = "train_nflx")) +
  geom_line(data = test_nflx, linewidth=1.5,
            aes(x = Date, y = `Adj Close`, col = "test_nflx")) +
  geom_point(data = tail(train_nflx, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 25, shape = 21, color = "black", fill="grey30") +
  labs(x = "\nPeriode (Tahun)", y='Saham Harga penutup',
       title = "Time Series Saham Netflix",
       subtitle = "Pembagian Data Training dan Test\n") +
  theme(legend.position = "none") +
  scale_colour_manual(values = c("train_nflx"=col.nflx[1], 
                                 "test_nflx"=col.nflx[2])) + 
  theme.ts1

chart

#Export Chart
ggsave("07_TS_NFLX_train-test.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 20)

Berdasarkan plot data deret waktu pada data latih (\(80\%\) dari data asli), terlihat bahwa data menunjukkan tren turun yang drastis dan tren naik yang cukup mulus. Ini mengisyaratkan bahwa data latih tidak memenuhi kriteria stasioneritas dalam rataan maupun ragam. Di sisi lain, dalam plot data uji (\(20\%\) dari data asli), terlihat adanya tren naik dan kurangnya nilai tengah yang stabil. Ini juga menunjukkan bahwa data uji tidak stasioner dalam rataan.

2.1.8 NVDA

nvda <- data2 %>%
  filter(Name == "NVDA")  # Filter data saham Amazon tahun 2022 ke atas

rownames(nvda) <- NULL
str(nvda)
## 'data.frame':    1044 obs. of  3 variables:
##  $ Name     : chr  "NVDA" "NVDA" "NVDA" "NVDA" ...
##  $ Date     : Date, format: "2021-01-01" "2021-01-02" ...
##  $ Adj Close: num  130 131 131 131 134 ...
datatable(nvda, filter = 'top', 
          options = list(pageLength = 5))

Mengubah Ajd Close Menjadi Time series.

nvda.ts <- ts(nvda[,3])

Ringkasan Data Ajd CLose.

summary(nvda.ts)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   112.2   158.1   206.4   237.1   278.5   493.5
min_value <- min(nvda$`Adj Close`)
min_date <- nvda$Date[which.min(nvda$`Adj Close`)]
percentage <- (which.min(nvda$`Adj Close`) / nrow(nvda)) * 100

chart <-
ggplot(nvda, aes(x=Date, y=`Adj Close`)) + 
  geom_line(aes(color=Name), linewidth=2) +
  scale_color_manual(values = col.nvda[1]) +
  labs(x = "\nPeriode (Tahun)", y='Saham Harga penutup',
       title = "Time Series Saham NVIDIA",
       subtitle = "Seperti apa sih pola deret waktu saham NVIDIA?\n") +
  theme(legend.position = "none") +
  theme.ts1 + 
  geom_vline(xintercept = as.numeric(min_date), 
             linetype = "dotted", color = "grey30", linewidth = 1.5) +
  geom_text(aes(x = min_date-1*40, y = max(`Adj Close`)*80/100, label = 
                  paste0("Titik Terendah\n","(",round(percentage, 2), "%)",
                         "   ",min_date)), 
            vjust = -1.5, hjust = 0, size = 7, color = "grey30") 
chart

#Export Chart
ggsave("08_Time Series NVIDIA.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 20)

Berdasarkan grafik deret waktu yang disajikan, terlihat bahwa sekitar \(50.25\%\) dari data menunjukkan kecenderungan tren menurun. Kemudian, pada tanggal 2022-10-14, terjadi perubahan pola menjadi tren naik dan menaik tajam pada Juli 2023.

Pembagian Data Training Dan Test.

#membagi 80% data latih (training) dan 20% data uji (testing)
train_nvda <- nvda[1: round(nrow(nvda) *78/100),]
test_nvda <- nvda[round(nrow(nvda) *78/100  +1): nrow(nvda),]
train_nvda.ts <- ts(train_nvda[,3])
test_nvda.ts <- ts(test_nvda[,3])
chart <-
ggplot() + 
  geom_line(data = train_nvda, linewidth=2,
            aes(x = Date, y = `Adj Close`, col = "train_nvda")) +
  geom_line(data = test_nvda, linewidth=2,
            aes(x = Date, y = `Adj Close`, col = "test_nvda")) +
  geom_point(data = tail(train_nvda, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 25, shape = 21, color = "black", fill="grey30") +
  labs(x = "\nPeriode (Tahun)", y='Saham Harga penutup',
       title = "Time Series Saham NVIDIA",
       subtitle = "Pembagian Data Training dan Test\n") +
  theme(legend.position = "none") +
  
  scale_colour_manual(values = c("train_nvda"=col.nvda[1], 
                                 "test_nvda"=col.nvda[2])) + 
  theme.ts1
  
chart

#Export Chart
ggsave("08_TS_NVDA_train-test.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 20)

Berdasarkan plot data deret waktu pada data latih (\(80\%\) dari data asli), terlihat bahwa data menunjukkan tren turun dan tren naik yang keduanya mulus. Ini mengisyaratkan bahwa data latih tidak memenuhi kriteria stasioneritas dalam rataan maupun ragam. Di sisi lain, dalam plot data uji (\(20\%\) dari data asli), terlihat adanya tren yang melonjak naik dan kurangnya nilai tengah yang stabil. Ini juga menunjukkan bahwa data uji tidak stasioner dalam rataan.

2.2 Data Train vs Test

chart <-
ggplot() + 
  #Label Data Asli 
  annotate( "rect", alpha=0.1, fill="seagreen",
            xmin=as.Date(min(data2$Date)), 
            xmax=as.Date(data2$Date[nrow(train_goog)]),
            ymin=0, ymax=Inf ) + 
  
  annotate( "text", color="seagreen",
            x = as.Date(data2$Date[ceiling(nrow(train_goog)/2)]), 
            y = max(data2$`Adj Close`) * 0.95, 
    label = "Data Latih", size=10) + 
  
  #Label Data Ramal
  annotate( "rect", alpha=0.1, fill="violetred",
            xmin=as.Date(data2$Date[nrow(train_meta)]), 
            xmax=as.Date(max(data2$Date)),
            ymin=0, ymax=Inf ) + 
  
  annotate( "text", color="violetred",
            x = as.Date(data2$Date[ceiling(0.925 * length(data2$Date)/7)]) , 
            y = max(data2$`Adj Close`) * 0.95, 
    label = "Data Uji", size=10) +
  
#AAPL
geom_line(data = train_aapl, linewidth=2,
            aes(x = Date, y = `Adj Close`, col = "AAPL")) +
  geom_line(data = test_aapl, linewidth=1.5,
            aes(x = Date, y = `Adj Close`, col = "test_aapl")) +
  geom_point(data = tail(train_aapl, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 15, shape = 21, color = "black", fill="violetred") +
  
#AMZN
  geom_line(data = train_amzn, linewidth=2,
            aes(x = Date, y = `Adj Close`, col = "AMZN")) +
  geom_line(data = test_amzn, linewidth=1.5,
            aes(x = Date, y = `Adj Close`, col = "test_amzn")) +
  geom_point(data = tail(train_amzn, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 15, shape = 21, color = "black", fill="violetred") +
#GOOG
  geom_line(data = train_goog, linewidth=2,
            aes(x = Date, y = `Adj Close`, col = "GOOG")) +
  geom_line(data = test_goog, linewidth=1.5,
            aes(x = Date, y = `Adj Close`, col = "test_goog")) +
  geom_point(data = tail(train_goog, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 15, shape = 21, color = "black", fill="violetred") +
  
#META
  geom_line(data = train_meta, linewidth=2,
            aes(x = Date, y = `Adj Close`, col = "META")) +
  geom_line(data = test_meta, linewidth=1.5,
            aes(x = Date, y = `Adj Close`, col = "test_meta")) +
  geom_point(data = tail(train_meta, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 15, shape = 21, color = "black", fill="violetred") +
 
#MSFT
  geom_line(data = train_msft, linewidth=2,
            aes(x = Date, y = `Adj Close`, col = "MSFT")) +
  geom_line(data = test_msft, linewidth=1.5,
            aes(x = Date, y = `Adj Close`, col = "test_msft")) +
  geom_point(data = tail(train_msft, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 15, shape = 21, color = "black", fill="violetred") +
   
#NFLx
  geom_line(data = train_nflx, linewidth=2,
            aes(x = Date, y = `Adj Close`, col = "NFLX")) +
  geom_line(data = test_nflx, linewidth=1.5,
            aes(x = Date, y = `Adj Close`, col = "test_nflx")) +
  geom_point(data = tail(train_nflx, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 15, shape = 21, color = "black", fill="violetred") +

#NVDA
  geom_line(data = train_nvda, linewidth=2,
            aes(x = Date, y = `Adj Close`, col = "NVDA")) +
  geom_line(data = test_nvda, linewidth=2,
            aes(x = Date, y = `Adj Close`, col = "test_nvda")) +
  geom_point(data = tail(train_nvda, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 15, shape = 21, color = "black", fill="violetred") +
  scale_colour_manual(values = cols) +
    theme.ts + #THeme
  labs(x = "\nPeriode (Tahun)", y='Harga Saham (USD)',
       title = "Time Series MAANG",
       subtitle = "Pembagian Data Latih dan data Uji\n") +
  # Label / legend
  geom_text_repel(
    data=data2[data2$Date == max(data2$Date),], #Posisi di ujung data
    aes(x=Date, y=`Adj Close`, 
        color=Name, label=Name), #Warna garis & label saham
    size = 8, #Ukuran text
    nudge_x = 20, #Posisi Text (kanan 50)
    hjust = 0, #Ujung
    segment.size = 1,               #Ukuran garis
    segment.alpha = .75,             #transparasi garis
    segment.linetype = "dotted",    #Time garis
    box.padding = .4, #Biar label saham nggak dempetan
    segment.curvature = -0.1, #biar garis mulus
    segment.ncp = 8, 
    segment.angle = 60 
  ) +
  #Axis
    coord_cartesian(clip = "off"
  ) +
    scale_x_date( #Sumbu x
    date_breaks = "1 year",  # Menampilkan label setiap tahun
    date_labels = "%Y",  # Format label tahun
    limits = c(as.Date(min(data2$Date)), 
               as.Date(max(data2$Date)) + 120)
    #Tampilin lebih dari 20023-07-28 agar label saham bisa masuk
  ) +
    scale_y_continuous( #Sumbu y
    labels = scales::dollar_format(prefix = "$") #tambahin dolar
  ) +
    annotate( #Buat nandain batas data
    "text", x = as.Date(max(data2$Date)), y = 50, 
    label = max(data2$Date), size=6
  ) +
  geom_vline( #Buat garis batas data
    xintercept = as.numeric(as.Date( max(data2$Date) )), 
             linetype = "dotted", color = "red") 
  
chart

#Export Chart
ggsave("09_banding_train-test.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 27)

3 Stasioneritas

3.1 Uji Stasioneritas

3.1.1 Plot ACF

install_load('tsibble','tseries')
par(mfrow=c(2,4))
acf(train_aapl.ts, main=""); mtext("AAPL", side=3, line=1, cex=2, font=2)
acf(train_amzn.ts, main=""); mtext("AMZN", side=3, line=1, cex=2, font=2)
acf(train_goog.ts, main=""); mtext("GOOG", side=3, line=1, cex=2, font=2)
acf(train_meta.ts, main=""); mtext("META", side=3, line=1, cex=2, font=2)
acf(train_msft.ts, main=""); mtext("MSFT", side=3, line=1, cex=2, font=2)
acf(train_nflx.ts, main=""); mtext("NFLX", side=3, line=1, cex=2, font=2)
acf(train_nvda.ts, main=""); mtext("NVDA", side=3, line=1, cex=2, font=2)

Berdasarkan plot ACF, terlihat bahwa plot ACF seluruh data train menurun secara perlahan (tails of slowly). Hal ini juga menjadi indikasi bahwa data tidak stasioner dalam rataan dan tidak membentuk gelombang sinus.

3.1.2 Uji ADF

adf <- data.frame(
  p.value = c(tseries::adf.test(train_aapl.ts)[["p.value"]],
               tseries::adf.test(train_amzn.ts)[["p.value"]],
               tseries::adf.test(train_goog.ts)[["p.value"]],
               tseries::adf.test(train_meta.ts)[["p.value"]],
               tseries::adf.test(train_msft.ts)[["p.value"]],
               tseries::adf.test(train_nflx.ts)[["p.value"]],
               tseries::adf.test(train_nvda.ts)[["p.value"]]
              )
) %>%
  mutate(across(where(is.numeric), ~round(., 2)))
adf$Keterangan <- ifelse(adf$p.value < 0.05, "Rataan Stasioner", 
                         "Rataan Tidak Stasioner")
rownames(adf) <- c("AAPL", "AMZN", "GOOG", "META", "MSFT", "NFLX", "NVDA")
datatable(adf, filter = 'top', 
          options = list(pageLength = 7))

\(H_0\) : Data tidak stasioner dalam rataan

\(H_1\) : Data stasioner dalam rataan

Berdasarkan uji ADF tersebut, semua p-value lebih besar dari taraf nyata \(5\%\) sehingga tak tolak \(H_0\) dan menandakan bahwa data tidak stasioner dalam rataan. Hal ini sesuai dengan hasil eksplorasi menggunakan plot time series dan plot ACF, sehingga ketidakstasioneran model kedepannya harus ditangani.

3.1.3 Plot Box-Cox

install_load('MASS')
par(mfrow=c(2,4))

index <- seq(1:nrow(train_aapl))
bc =boxcox(train_aapl.ts~index, lambda=seq(-2, 4, by=.01))
title("AAPL", cex.main = 4)
lambda_aapl <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_aapl <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

index <- seq(1:nrow(train_amzn))
bc =boxcox(train_amzn.ts~index, lambda=seq(-2, 4, by=.01))
title("AMZN", cex.main = 4)
lambda_amzn <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_amzn <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

index <- seq(1:nrow(train_goog))
bc =boxcox(train_goog.ts~index, lambda=seq(-2, 4, by=.01))
title("GOOG", cex.main = 4)
lambda_goog <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_goog <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

index <- seq(1:nrow(train_meta))
bc =boxcox(train_meta.ts~index, lambda=seq(-2, 4, by=.01))
title("META", cex.main = 4)
lambda_meta <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_meta <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

index <- seq(1:nrow(train_msft))
bc =boxcox(train_msft.ts~index, lambda=seq(-2, 4, by=.01))
title("MSFT", cex.main = 4)
lambda_msft <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_msft <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

index <- seq(1:nrow(train_nflx))
bc =boxcox(train_nflx.ts~index, lambda=seq(-2, 4, by=.01))
title("NFLX", cex.main = 4)
lambda_nflx <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_nflx <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

index <- seq(1:nrow(train_nvda)) #Beda titik potong
bc =boxcox(train_nvda.ts~index, lambda=seq(-2, 4, by=.01))
title("NVDA", cex.main = 4)
lambda_nvda <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_nvda <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

Gambar di atas menunjukkan bahwa dari rentang nilai lambda -2 hingga 4, hanya NFLX yang memuat rounded value (\(\lambda\)) = 1 di dalam selangnya. Sementara selainnya tidak memuatnya.

3.1.3.1 Lambda dan Selang Kepercayaan

lsk <- data.frame(
  Lambda = c(lambda_aapl, lambda_amzn, lambda_goog, lambda_meta, 
             lambda_msft, lambda_nflx, lambda_nvda ),
  Batas.Bawah = c(min(sk_aapl), min(sk_amzn), min(sk_goog), min(sk_meta),
                  min(sk_msft), min(sk_nflx), min(sk_nvda)  ),
  Batas.Atas = c(max(sk_aapl), max(sk_amzn), max(sk_goog), max(sk_meta),
                 max(sk_msft), max(sk_nflx), max(sk_nvda)  )
) %>%
  mutate(across(where(is.numeric), ~round(., 2)))

lsk$Keterangan <- apply(lsk, 1, function(row) {
  ifelse(row["Batas.Bawah"] <= 1 && row["Batas.Atas"] >= 1,
         "Ragam Stasioner",  "Ragam Tidak Stasioner")
})

rownames(lsk) <- c("AAPL", "AMZN", "GOOG", "META", "MSFT", "NFLX", "NVDA")

datatable(lsk, filter = 'top', 
          options = list(pageLength = 7))

Hal ini dibuktikan dengan tabel nilai rounded value (\(\lambda\)) dan selang kepercayaan diatas yang menyatakan bahwa selain NFLX tidak ada yang memuat nilai satu di dalam selangnya. Ini mengindikasikan bahwa hanya NFLX yang memiliki stasioneritas dalam ragam, Sementara yang lain memerlukan transformasi data karena tidak memiliki sifat stasioner dalam ragam.

3.2 Penanganan Ketidakstasioneran Data

3.2.1 Dalam Ragam: Transformasi

Mengutip dari laman r-coder.com, formula transformasi data disesuaikan dengan nilai rounded value (\(\lambda\)) optimumnya.

Namun dari laman tersebut dan dari laman robjhyndman.com mengatakan bahwa rounded value (\(\lambda\)) = 1 itu cenderung tidak ada perubahan substansial atau signifikan yang terjadi pada data. Sehingga jika transformasi diatas tidak menyelesaikan masalah stasioneritas ragam maka alternatif transfromasi yang dapat dilakukan adalah dengan \((x^\lambda -1 )/\lambda\) dengan transformasi balik \((\lambda x+1)^{1/\lambda}\).
\(\mathbf{\lambda}\) Transformasi Transformasi Balik
-2 \(1/x^2\) \(1/\sqrt{x}\)
-1 \(1/x\) \(1/x\)
-0.5 \(1/\sqrt{x}\) \(1/x^2\)
0 \(\log{(x)}\) \(e^x\)
0.5 \(\sqrt{x}\) \(x^2\)
1 \(x\) \(x\)
2 \(x^2\) \(\sqrt{x}\)

Sehingga transformasi yang digunakan pada data saham yang tidak stasioner dalam ragam adalah sebagai berikut.

lamb <- c(-2, -1, -.5, 0, .5, 1, 2)
# Fungsi untuk mengonversi lambda ke transformasi
trans <- function(lambda) {
  lambda <- lamb[sapply(lambda, function(y) which.min(abs(y - lamb))) ]
  case_when(
    lambda == -2   ~ "1/x^2",
    lambda == -1   ~ "1/x",
    lambda == -0.5 ~ "1/sqrt(x)",
    lambda == 0    ~ "log(x)",
    lambda == 1    ~ "x",
    lambda == 0.5  ~ "sqrt(x)",
    lambda == 2    ~ "x^2",
    TRUE           ~ NA_character_
  )
}

# Fungsi untuk mengonversi lambda ke transformasi balik
trans_balik <- function(lambda) {
  lambda <- lamb[sapply(lambda, function(y) which.min(abs(y - lamb))) ]
  case_when(
    lambda == -2   ~ "1/sqrt(x)",
    lambda == -1   ~ "1/x",
    lambda == -0.5 ~ "1/x^2",
    lambda == 0    ~ "exp(x)",
    lambda == 1    ~ "x",
    lambda == 0.5  ~ "x^2",
    lambda == 2    ~ "sqrt(x)",
    TRUE           ~ NA_character_
  )
}

# Menambahkan kolom Transformasi dan Trans Balik
tran <- lsk %>%
  mutate(
    Transformasi = trans(Lambda),
    `Transformasi Balik` = trans_balik(Lambda)
  ) %>% dplyr::select(Lambda, Transformasi, `Transformasi Balik`)

tran[6,c(2,3)] <- "-"

datatable(tran, filter = 'top', 
          options = list(pageLength = 7))

3.2.1.1 Transformasi

#Transformasi
train_aapl.ts.new <- 1/sqrt(train_aapl.ts)
train_amzn.ts.new <- (train_amzn.ts)
train_goog.ts.new <- sqrt(train_goog.ts)
train_meta.ts.new <- sqrt(train_meta.ts)
train_msft.ts.new <- 1/(train_msft.ts)
train_nvda.ts.new <- 1/sqrt(train_nvda.ts)

#Plot BoxCox
par(mfrow=c(2,3))
index <- seq(1:nrow(train_aapl))
bc <-boxcox(train_aapl.ts.new ~index, lambda=seq(-2, 4, by=.01))
title("AAPL", cex.main=3)
lambda_aapl.new <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_aapl.new <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

index <- seq(1:nrow(train_amzn))
bc <-boxcox(train_amzn.ts.new ~index, lambda=seq(-2, 4, by=.01))
title("AMZN", cex.main=3)
lambda_amzn.new <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_amzn.new <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

index <- seq(1:nrow(train_goog))
bc <-boxcox(train_goog.ts.new ~index, lambda=seq(-2, 4, by=.01))
title("GOOG", cex.main=3)
lambda_goog.new <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_goog.new <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

index <- seq(1:nrow(train_meta))
bc <-boxcox(train_meta.ts.new ~index, lambda=seq(-2, 4, by=.01))
title("META", cex.main=3)
lambda_meta.new <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_meta.new <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

index <- seq(1:nrow(train_msft))
bc <-boxcox(train_msft.ts.new ~index, lambda=seq(-2, 4, by=.01))
title("MSFT", cex.main=3)
lambda_msft.new <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_msft.new <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

index <- seq(1:nrow(train_nvda))
bc <-boxcox(train_nvda.ts.new ~index, lambda=seq(-2, 4, by=.01))
title("NVDA", cex.main=3)

lambda_nvda.new <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_nvda.new <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

Setelah melalui proses transformasi, gambar di atas mengindikasikan bahwa setiap rangkaian data sekarang mencakup nilai satu dalam selangnya, yang menunjukkan bahwa data tersebut sekarang sudah stasioner dalam ragam.

lsk.new <- data.frame(
  Lambda = c(lambda_aapl.new, lambda_amzn.new, lambda_goog.new, 
             lambda_meta.new, lambda_msft.new, lambda_nvda.new ),
  Batas.Bawah = c(min(sk_aapl.new), min(sk_amzn.new), min(sk_goog.new), 
                  min(sk_meta.new), min(sk_msft.new), min(sk_nvda.new)  ),
  Batas.Atas = c(max(sk_aapl.new), max(sk_amzn.new), max(sk_goog.new), 
                 max(sk_meta.new), max(sk_msft.new), max(sk_nvda.new)  )
) %>%
  mutate(across(where(is.numeric), ~round(., 2)))

lsk.new$Keterangan <- apply(lsk.new, 1, function(row) {
  ifelse(row["Batas.Bawah"] <= 1 && row["Batas.Atas"] >= 1,
         "Ragam Stasioner",  "Ragam Tidak Stasioner")
})

rownames(lsk.new) <- c("AAPL", "AMZN", "GOOG", "META", "MSFT", "NVDA")

datatable(lsk.new, filter = 'top', 
          options = list(pageLength = 7))

Terlihat bahwa data AMZN dan META belum stasioner. Sehingga akan dilakukan transformasi ulang dengan \((x^\lambda -1 )/\lambda\) dengan transformasi balik \((\lambda x+1)^{1/\lambda}\).

#Transformasi
train_aapl.ts.new <- 1/sqrt(train_aapl.ts)
train_amzn.ts.new <- (train_amzn.ts^lambda_amzn - 1)/lambda_amzn
train_goog.ts.new <- sqrt(train_goog.ts)
train_meta.ts.new <- (train_meta.ts^lambda_meta - 1)/lambda_meta
train_msft.ts.new <- 1/(train_msft.ts)
train_nvda.ts.new <- 1/sqrt(train_nvda.ts)

#Plot BoxCox
par(mfrow=c(2,3))
index <- seq(1:nrow(train_aapl))
bc <-boxcox(train_aapl.ts.new ~index, lambda=seq(-2, 4, by=.01))
title("AAPL", cex.main=3)
lambda_aapl.new <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_aapl.new <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

index <- seq(1:nrow(train_amzn))
bc <-boxcox(train_amzn.ts.new ~index, lambda=seq(-2, 4, by=.01))
title("AMZN", cex.main=3)
lambda_amzn.new <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_amzn.new <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

index <- seq(1:nrow(train_goog))
bc <-boxcox(train_goog.ts.new ~index, lambda=seq(-2, 4, by=.01))
title("GOOG", cex.main=3)
lambda_goog.new <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_goog.new <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

index <- seq(1:nrow(train_meta))
bc <-boxcox(train_meta.ts.new ~index, lambda=seq(-2, 4, by=.01))
title("META", cex.main=3)
lambda_meta.new <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_meta.new <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

index <- seq(1:nrow(train_msft))
bc <-boxcox(train_msft.ts.new ~index, lambda=seq(-2, 4, by=.01))
title("MSFT", cex.main=3)
lambda_msft.new <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_msft.new <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

index <- seq(1:nrow(train_nvda))
bc <-boxcox(train_nvda.ts.new ~index, lambda=seq(-2, 4, by=.01))
title("NVDA", cex.main=3)

lambda_nvda.new <- bc$x[which.max(bc$y)] #Nilai Rounded Lambda
sk_nvda.new <- bc$x[bc$y > max(bc$y) - 1/2 * qchisq(.95,1)] #SK

Terlihat bahwa data sudah stasioner dalam ragam.

lsk.new <- data.frame(
  Lambda = c(lambda_aapl.new, lambda_amzn.new, lambda_goog.new, 
             lambda_meta.new, lambda_msft.new, lambda_nflx,
             lambda_nvda.new ),
  Batas.Bawah = c(min(sk_aapl.new), min(sk_amzn.new), min(sk_goog.new), 
                  min(sk_meta.new), min(sk_msft.new), min(sk_nflx),
                  min(sk_nvda.new)  ),
  Batas.Atas = c(max(sk_aapl.new), max(sk_amzn.new), max(sk_goog.new), 
                 max(sk_meta.new), max(sk_msft.new), max(sk_nflx),
                 max(sk_nvda.new)  )
) %>%
  mutate(across(where(is.numeric), ~round(., 2)))

lsk.new$Keterangan <- apply(lsk.new, 1, function(row) {
  ifelse(row["Batas.Bawah"] <= 1 && row["Batas.Atas"] >= 1,
         "Ragam Stasioner",  "Ragam Tidak Stasioner")
})

rownames(lsk.new) <- c("AAPL", "AMZN", "GOOG", "META", "MSFT", "NFLX", "NVDA")

lsk.new <- cbind(lsk.new, tran[,-1])

lsk.new[which(rownames(lsk.new) %in% c("META", "AMZN")), c("Transformasi",       "Transformasi Balik")] <- list("(x^lamb - 1)/ lamb", "(lamb x + 1)^{1/lamb}")

datatable(lsk.new, filter = 'top', 
          options = list(pageLength = 7))

Ini juga dibuktikan dengan tabel diatas.

3.2.2 Dalam Rataan: Differencing

Differencing (diferensiasi) adalah suatu proses statistik yang digunakan untuk mengatasi ketidakstasioneran dalam data rata-rata atau tren data. Tujuannya adalah untuk membuat data menjadi lebih stasioner dengan mengurangi atau menghilangkan tren atau pola waktu yang mungkin ada dalam data tersebut.

#Diff
train_aapl.diff <- diff(train_aapl.ts.new, differences = 1) 
train_amzn.diff <- diff(train_amzn.ts.new, differences = 1) 
train_goog.diff <- diff(train_goog.ts.new, differences = 1) 
train_meta.diff <- diff(train_meta.ts.new, differences = 1) 
train_msft.diff <- diff(train_msft.ts.new, differences = 1) 
train_nflx.diff <- diff(train_nflx.ts, differences = 1) 
train_nvda.diff <- diff(train_nvda.ts.new, differences = 1) 

#Plot
par(mfrow=c(2,4))
plot.ts(train_aapl.diff, lty=1, xlab="Periode (Tahun)", 
        col = "lightskyblue4", lwd = 2,
        main="AAPL", cex.main = 4)
plot.ts(train_amzn.diff, lty=1, xlab="Periode (Hari)", 
        col = "orange2", lwd = 2,
        main="AMZN", cex.main = 4)
plot.ts(train_goog.diff, lty=1, xlab="Differencing  (Hari)", 
        col = "gold3", lwd = 2,
        main="GOOG", cex.main = 4)
plot.ts(train_meta.diff, lty=1, xlab="Periode (Tahun)", 
        col = "dodgerblue3", lwd = 2,
        main="META", cex.main = 4)
plot.ts(train_msft.diff, lty=1, xlab="Periode (Tahun)", 
        col = "deepskyblue2", lwd = 2,
        main="MSFT", cex.main = 4)
plot.ts(train_nflx.diff, lty=1, xlab="Periode (Hari)", 
        col = "firebrick3", lwd = 2,
        main="NFLX", cex.main = 4)
plot.ts(train_nvda.diff, lty=1, xlab="Periode (Tahun)", 
        col = "green4", lwd = 2,
        main="NVDA", cex.main = 4)

Berdasarkan plot data deret waktu yang sudah di Differencing, terlihat bahwa seluruh data sudah stasioner dalam rataan ditandai dengan data bergerak pada nilai tengah tertentu (tidak terdapat trend ataupun musiman pada data).

3.3 Uji Ulang

Akan dilakukan uji ulang untuk mengecek kestasioneran data dalam rataan.

3.3.1 Plot ACF

par(mfrow=c(2,4))
acf(train_aapl.diff, ylim=c(-.1,.1) , main="")
mtext("AAPL", side=3, line=1, cex=2, font=2)

acf(train_amzn.diff, ylim=c(-.1,.1) , main="")
mtext("AMZN", side=3, line=1, cex=2, font=2)

acf(train_goog.diff, ylim=c(-.1,.1) , main="")
mtext("GOOG", side=3, line=1, cex=2, font=2)

acf(train_meta.diff, ylim=c(-.1,.1) , main="")
mtext("META", side=3, line=1, cex=2, font=2)

acf(train_msft.diff, ylim=c(-.1,.1) , main="")
mtext("MSFT", side=3, line=1, cex=2, font=2)

acf(train_nflx.diff, ylim=c(-.1,.1) , main="")
mtext("NFLX", side=3, line=1, cex=2, font=2)

acf(train_nvda.diff, ylim=c(-.1,.1) , main="")
mtext("NVDA", side=3, line=1, cex=2, font=2)

Berdasarkan ketujuh plot ACF tersebut, dapat dilihat bahwa lag nya sudah tidak tails off slowly (trennya telah berakhir dan korelasi antara pengamatan pada lag-lag yang lebih jauh telah berkurang secara signifikan), sehingga data dinyatakan stasioner dalam rataan. Ini menunjukkan bahwa masalah ketidakstasioneran dalam data telah berhasil diatasi.

3.3.2 Uji ADF

adf1 <- data.frame(
  p.value = c(tseries::adf.test(train_aapl.diff)[["p.value"]],
              tseries::adf.test(train_amzn.diff)[["p.value"]],
              tseries::adf.test(train_goog.diff)[["p.value"]],
              tseries::adf.test(train_meta.diff)[["p.value"]],
              tseries::adf.test(train_msft.diff)[["p.value"]],
              tseries::adf.test(train_nflx.diff)[["p.value"]],
              tseries::adf.test(train_nvda.diff)[["p.value"]]
              )
)
adf1$Keterangan <- ifelse(adf1$p.value < 0.05, "Rataan Stasioner", 
                         "Rataan Tidak Stasioner")
rownames(adf1) <- c("AAPL", "AMZN", "GOOG", "META", "MSFT", "NFLX", "NVDA")
datatable(adf1, filter = 'top', 
          options = list(pageLength = 7))

\(H_0\) : Data tidak stasioner dalam rataan

\(H_1\) : Data stasioner dalam rataan

Berdasarkan uji ADF tersebut, semua data saham mendapatkan p-value sebesar \(0.01\) yang lebih kecil dari taraf nyata \(5\%\) sehingga tolak \(H_0\) atau data stasioner dalam rataan. Hal ini sesuai dengan hasil eksplorasi menggunakan plot time series dan plot ACF, sehingga dalam hal ini ketidakstasioneran data sudah berhasil ditangani dan dapat dilanjutkan ke pemodelan.

4 Model Tentatif

Identifikasi model tentatif dapat dilakukan dengan melihat pada plot ACF, PACF dan Tabel EACF. Mengutip dari postingan pada laman medium.com Panduan dasar untuk menginterpretasikan plot ACF dan PACF adalah sebagai berikut :

  1. Cari pola tail off pada ACF atau PACF.
  2. Jika tail off terjadi pada ACF → model ARcut off pada PACF akan memberikan nilai p untuk AR(p).
  3. Jika tail off terjadi pada PACF → model MAcut off pada ACF akan memberikan nilai q untuk MA(q).
  4. Jika terjadi tail off pada kedua ACF dan PACF → model ARMA.

4.1 Plot ACF & PACF

par(mfrow=c(2,4))
pacf(train_aapl.diff, ylim=c(-.1,.1) , main="")
mtext("AAPL", side=3, line=1, cex=2, font=2)

pacf(train_amzn.diff, ylim=c(-.1,.1) , main="")
mtext("AMZN", side=3, line=1, cex=2, font=2)

pacf(train_goog.diff, ylim=c(-.1,.1) , main="")
mtext("GOOG", side=3, line=1, cex=2, font=2)

pacf(train_meta.diff, ylim=c(-.1,.1) , main="")
mtext("META", side=3, line=1, cex=2, font=2)

pacf(train_msft.diff, ylim=c(-.1,.1) , main="")
mtext("MSFT", side=3, line=1, cex=2, font=2)

pacf(train_nflx.diff, ylim=c(-.1,.1) , main="")
mtext("NFLX", side=3, line=1, cex=2, font=2)

pacf(train_nvda.diff, ylim=c(-.1,.1) , main="")
mtext("NVDA", side=3, line=1, cex=2, font=2)

Referensi : Significance level of ACF and PACF in R

acf.pacf <- function(dt){ #Fungsi Deteksi Cut off plot ACF & PACF
  N <- acf(dt, plot = F)[["n.used"]] + 1 #Banyaknya Data
  #Garis Signifikan
  sl <- c(-1,1)*(exp(2*1.96/sqrt(N-3))-1)/(exp(2*1.96/sqrt(N-3))+1)
  acf <- data.frame( lag = acf(dt, plot=F)[["lag"]], 
                     acf = acf(dt, plot=F)[["acf"]] ) 
  
    cut.off1 <- acf %>%
        filter(acf <= min(sl) | acf >= max(sl)) %>%
        dplyr::select(lag); colnames(cut.off1) <- "acf"
  
  pacf <- data.frame(lag = pacf(dt, plot=F)[["lag"]], 
                     acf = pacf(dt, plot=F)[["acf"]] ) 
  
    cut.off2 <- pacf %>%
        filter(acf <= min(sl) | acf >= max(sl)) %>%
        dplyr::select(lag); colnames(cut.off2) <- "pacf"
    
  cut.off <- merge(cut.off1, cut.off2, by = 0, all = TRUE)[-1]
  return(cut.off)
}

data_frames <- list(
  train_aapl.diff, train_amzn.diff, train_goog.diff,
  train_meta.diff, train_msft.diff, train_nflx.diff,
  train_nvda.diff
)

name <- unique(data$Name)

acf.pacf.tab <- data.frame()
for(i in 1:length(name)){
  dex <- data_frames[[i]] %>% acf.pacf() %>% 
  rename(!!paste0(name[i], ".a") := acf, 
         !!paste0(name[i], ".p") := pacf)
  
    acf.pacf.tab <- merge(acf.pacf.tab, dex, by=0, all=TRUE)[-1]
}

datatable(acf.pacf.tab, filter = 'top', 
          options = list(pageLength = 7))

Berdasarkan plot tersebut, terlihat bahwa plot ACF cuts off pada lag 13, dan lag 14. Sehingga model tidak dapat di identifikasi dengan plot ACF.

4.2 Tabel EACF

Referensi : [University of South Carolina] Chapter 6: Model Specification for Time Series

Mengutip dari referensi diatas, Identifikasi model menggunakan Tabel EACF (Extended Autocorrelation Function) untuk proses ARMA(p, q) seharusnya secara teoritis memiliki pola segitiga nol, dengan nol di sudut kiri atas muncul pada baris ke-p dan kolom ke-q (dengan label baris dan kolom dimulai dari 0).

Sebagai contoh :

Dalam hal ini model tentatif yang terbentuk adalah ARIMA(0,1,1), ARIMA(1,1,1), ARIMA(2,1,2), ARIMA(3,1,3), dst.. Namun karena kemungkinannya sangat banyak, maka akan digunakan function agar menyingkat proses.

Pemilihan model ARIMA terbaik dilakukan ketika memiliki nilai AIC yang terkecil, Semua Parameter Signifikan, dan Tidak ada Parameter NA.

install_load('TSA', 'forecast')

# Fungsi untuk menghitung model ARIMA dan menganalisis parameter
model_tentatif <- function(data, p_max, d, q_max, alpha=0.05, drift=FALSE) {
  best_model <- NULL
  best_aic <- Inf
  eacf_result <- eacf(data) #Untuk Ektraksi & identifikasi model
  models <- data.frame(Model = character(0), 
                       AIC = numeric(0), 
                       Signif = character(0), 
                       Keterangan = character(0))
  
  for (p in 0:p_max) {
    for (q in 1:q_max) {
      #Pola Matriks segitiga bawah
      if (!is.na(eacf_result$symbol[p + 1, q ]) && 
          !is.na(eacf_result$symbol[p + 1, q + 1]) && 
          !is.na(eacf_result$symbol[p + 2, q + 1])) {
        if (eacf_result$symbol[p + 1, q ] == "o" && 
            eacf_result$symbol[p + 1, q + 1] == "o" && 
            eacf_result$symbol[p + 2, q + 1] == "o") {
      
          model <- Arima(data, order=c(p,d,q), method="ML", 
                         include.drift=drift)
          aic <- AIC(model)
          
          # Mendapatkan nilai coef dari model
          coeftest_result <- lmtest::coeftest(model)
          
          # jika lebih kecil dari alpha, maka signifikan
          significant_params <- 
            rownames(coeftest_result)[coeftest_result[, "Pr(>|z|)"] < alpha]  
          
          # jika lebih besar dari alpha, maka tidak signifikan
          non_significant_params <- 
            rownames(coeftest_result)[coeftest_result[, "Pr(>|z|)"] > alpha]  
          
          # Keterangan signifikansi
          if (length(significant_params) == 0) {
            keterangan <- "Semua parameter tidak signifikan"
          } else if (length(significant_params) == nrow(coeftest_result)) {
            keterangan <- "Semua parameter signifikan"
          } else {
            keterangan <- paste("Parameter yang tidak signifikan adalah", 
                                paste(non_significant_params, collapse = ", "))
          }
          
          models <- rbind(models, 
                    data.frame(Model = paste("ARIMA(", p, ",", d, ",", q, ")", 
                                             sep = ""), 
                               AIC = aic, 
                               Signif = paste(significant_params, 
                                              collapse = ", "), 
                               Keterangan = keterangan))
          
          #Identifikasi Best Model
            if (keterangan == "Semua parameter signifikan" && 
                !any(is.na(significant_params))) {
              if (aic < best_aic) {
                best_model <- model
                best_aic <- aic
            }
          }
        }
      }
    }
  }
  
  cat("\nModel ARIMA dengan AIC terkecil:\n")
  print(best_model)

  return(models)
}

4.2.1 AAPL

model.tentaif_aapl <- 
  model_tentatif(train_aapl.diff, p_max = 6, d = 1, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o o o o o o o o  o  x  x 
## 1 x o o o o o o o o o o  o  o  x 
## 2 x o o o o o o o o o o  o  o  o 
## 3 x o o o o o o o o o o  o  o  o 
## 4 x o o x o o o o o o o  o  o  o 
## 5 x x x o o o o o o o o  o  o  o 
## 6 x x x x o x o o o o o  o  o  o 
## 7 x o x x x o o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## Series: data 
## ARIMA(0,1,1) 
## 
## Coefficients:
##           ma1
##       -1.0000
## s.e.   0.0037
## 
## sigma^2 = 3.664e-07:  log likelihood = 4987.5
## AIC=-9971   AICc=-9970.98   BIC=-9961.55
datatable(model.tentaif_aapl, filter = 'top', 
          options = list(pageLength = 5))

Berdasarkan pendugaan parameter di atas, nilai AIC terkecil \(-9971\) dimiliki oleh model ARIMA(0,1,1) dan seluruh parameternya signifikan sehingga model yang dipilih adalah model ARIMA(0,1,1) .

model_aapl.da <- Arima(train_aapl.diff, order=c(0,1,1), method="ML")

4.2.2 AMZN

model.tentaif_amzn <- 
  model_tentatif(train_amzn.diff, p_max = 6, d = 1, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o o x o o o o o  o  o  o 
## 1 x o o o o o o o o o o  o  o  o 
## 2 o o o o o o o o o o o  o  o  o 
## 3 x o o o o o o o o o o  o  o  o 
## 4 x o o x o o o o o o o  o  o  o 
## 5 x x o x x o o o o o o  o  o  o 
## 6 x x x x x o o o o o o  o  o  o 
## 7 x x o o x x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## Series: data 
## ARIMA(0,1,1) 
## 
## Coefficients:
##           ma1
##       -1.0000
## s.e.   0.0039
## 
## sigma^2 = 199.8:  log likelihood = -3476.56
## AIC=6957.12   AICc=6957.13   BIC=6966.62
datatable(model.tentaif_amzn, filter = 'top', 
          options = list(pageLength = 5))

Berdasarkan pendugaan parameter di atas, nilai AIC terkecil \(6957.12\) dimiliki oleh model ARIMA(0,1,1) dan seluruh parameternya signifikan sehingga model yang dipilih adalah model ARIMA(0,1,1).

model_amzn.da <- Arima(train_amzn.diff, order=c(0,1,1), method="ML")

4.2.3 GOOG

model.tentaif_goog <- 
  model_tentatif(train_goog.diff, p_max = 6, d = 1, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o o o o o o o o  o  x  o 
## 1 x o o o o o o o o o o  o  o  o 
## 2 x x o o o o o o o o o  o  o  o 
## 3 x x x o o o o o o o o  o  o  o 
## 4 x x x o o o o o o o o  o  o  o 
## 5 x x x x x o o o o o o  o  o  o 
## 6 x o x x x x o o o o o  o  o  o 
## 7 x x x x x o o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## Series: data 
## ARIMA(4,1,5) 
## 
## Coefficients:
##           ar1      ar2      ar3      ar4      ma1      ma2     ma3     ma4
##       -0.8838  -0.1428  -0.9017  -0.9719  -0.0924  -0.7343  0.7407  0.0947
## s.e.   0.0156   0.0095   0.0065   0.0181   0.0226   0.0223  0.0097  0.0245
##           ma5
##       -0.9876
## s.e.   0.0250
## 
## sigma^2 = 0.007121:  log likelihood = 944.18
## AIC=-1868.36   AICc=-1868.11   BIC=-1820.38
datatable(model.tentaif_goog, filter = 'top', 
          options = list(pageLength = 5))

Berdasarkan pendugaan parameter di atas, nilai AIC terkecil \(-1868.36\) dimiliki oleh model ARIMA(4,1,5) dan seluruh parameternya signifikan sehingga model yang dipilih adalah model ARIMA(4,1,5).

model_goog.da <- Arima(train_goog.diff, order=c(4,1,5), method="ML")

4.2.4 META

model.tentaif_meta <- 
  model_tentatif(train_meta.diff, p_max = 6, d = 1, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o o o o o o o o  o  o  o 
## 1 x o o o o o o o o o o  o  o  o 
## 2 x o o o o o o o o o o  o  o  o 
## 3 o x o o o o o o o o o  o  o  o 
## 4 o x x x o o o o o o o  o  o  o 
## 5 x x o x x o o o o o o  o  o  o 
## 6 x x o x x x o o o o o  o  o  o 
## 7 x x x x x x x o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## Series: data 
## ARIMA(0,1,1) 
## 
## Coefficients:
##           ma1
##       -0.9943
## s.e.   0.0081
## 
## sigma^2 = 0.02284:  log likelihood = 370.6
## AIC=-737.2   AICc=-737.19   BIC=-727.86
datatable(model.tentaif_meta, filter = 'top', 
          options = list(pageLength = 5))

Berdasarkan pendugaan parameter di atas, nilai AIC terkecil \(-737.20\) dimiliki oleh model ARIMA(0,1,1) dan seluruh parameternya signifikan sehingga model yang dipilih adalah model ARIMA(0,1,1) .

model_meta.da <- Arima(train_meta.diff, order=c(0,1,1), method="ML")

4.2.5 MSFT

model.tentaif_msft <- 
  model_tentatif(train_msft.diff, p_max = 6, d = 1, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o x o o o o o o  o  o  x 
## 1 x o o o o x o o o o o  o  o  x 
## 2 x x o o o o o o o o o  o  o  o 
## 3 x o x o o o o o o o o  o  o  o 
## 4 x o x x o o o o o o o  o  o  o 
## 5 x x x x x o o o o o o  o  o  o 
## 6 x x x x x x o o o o o  o  o  o 
## 7 x x x o o x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## Series: data 
## ARIMA(4,1,5) 
## 
## Coefficients:
##           ar1      ar2      ar3      ar4      ma1      ma2     ma3     ma4
##       -0.5482  -0.1853  -0.5935  -0.9407  -0.4245  -0.3998  0.3989  0.3708
## s.e.   0.0554   0.0499   0.0478   0.0325   0.0537   0.0797  0.0735  0.0490
##           ma5
##       -0.9453
## s.e.   0.0295
## 
## sigma^2 = 3.058e-09:  log likelihood = 6984.45
## AIC=-13948.9   AICc=-13948.63   BIC=-13901.65
datatable(model.tentaif_msft, filter = 'top', 
          options = list(pageLength = 5))

Berdasarkan pendugaan parameter di atas, nilai AIC terkecil \(-13948.9\) dimiliki oleh model ARIMA(4,1,5) dan seluruh parameternya signifikan sehingga model yang dipilih adalah model ARIMA(4,1,5).

model_msft.da <- Arima(train_msft.diff, order=c(4,1,5), method="ML")

4.2.6 NFLX

model.tentaif_nflx <- 
  model_tentatif(train_nflx.diff, p_max = 6, d = 1, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o o o o o o o o  o  x  o 
## 1 x o o o o o o o o o o  o  x  o 
## 2 x x o o o o o o o o o  o  x  o 
## 3 x x x o o o o o o o o  o  x  o 
## 4 x x x x o o o o o o o  o  o  o 
## 5 x x x x o o o o o o o  o  o  o 
## 6 x x o x x x o o o o o  o  o  o 
## 7 x x x x x x x o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## Series: data 
## ARIMA(6,1,7) 
## 
## Coefficients:
##           ar1      ar2      ar3      ar4      ar5      ar6      ma1      ma2
##       -0.6120  -0.3786  -0.8290  -0.3410  -0.5277  -0.8922  -0.3384  -0.2326
## s.e.   0.0606   0.0673   0.0386   0.0582   0.0765   0.0540   0.0403   0.0177
##          ma3      ma4     ma5     ma6      ma7
##       0.4289  -0.4401  0.2150  0.3706  -0.9663
## s.e.  0.0376   0.0354  0.0324  0.0214   0.0439
## 
## sigma^2 = 93.48:  log likelihood = -3071.22
## AIC=6170.43   AICc=6170.95   BIC=6236.58
datatable(model.tentaif_nflx, filter = 'top', 
          options = list(pageLength = 5))

Berdasarkan pendugaan parameter di atas, nilai AIC terkecil \(6170.43\) dimiliki oleh model ARIMA(6,1,7) dan seluruh parameternya signifikan sehingga model yang dipilih adalah model ARIMA(6,1,7).

model_nflx.da <- Arima(train_nflx.diff, order=c(6,1,7), method="ML")

4.2.7 NVDA

model.tentaif_nvda <- 
  model_tentatif(train_nvda.diff, p_max = 6, d = 1, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o o o o o o o o  o  o  o 
## 1 x o o o o o o o o o o  o  o  o 
## 2 x x o o o o o o o o o  o  o  o 
## 3 x o x o o o o o o o o  o  o  o 
## 4 o x x x o o o o o o o  o  o  o 
## 5 x x x o x o o o o o o  o  o  o 
## 6 x x x x x x o o o o o  o  o  o 
## 7 x x o x x x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## Series: data 
## ARIMA(0,1,1) 
## 
## Coefficients:
##           ma1
##       -0.9933
## s.e.   0.0078
## 
## sigma^2 = 9.868e-07:  log likelihood = 4460.68
## AIC=-8917.35   AICc=-8917.34   BIC=-8907.96
datatable(model.tentaif_nvda, filter = 'top', 
          options = list(pageLength = 5))

Berdasarkan pendugaan parameter di atas, nilai AIC terkecil \(-8917.35\) dimiliki oleh model ARIMA(0,1,1) dan seluruh parameternya signifikan sehingga model yang dipilih adalah model ARIMA(0,1,1).

model_nvda.da <- Arima(train_nvda.diff, order=c(0,1,1), method="ML")

4.3 Model Tentatif Terbaik

model_terbaik <- function(model, rowname, mods) {
  if (!all(is.na(model))) {
    model_df <- data.frame(
      Model = paste("ARIMA(", 
                     paste(as.character(model[["call"]][["order"]][-1]), 
                           collapse = ","), ")", sep = ""),
      AIC = model[["aicc"]],
      row.names = rowname
    )
    model_df$Keterangan <- 
      mods[which(mods$Model == 
                     model_df["Model"][rowname,]), "Keterangan"]
  return(model_df)
    
  } else {
    return(data.frame(
      Model = NA_character_,
      AIC = NA_real_,
      Keterangan = NA_character_,
      row.names = rowname
    ))
  }
}

tentatif <- rbind(
  model_terbaik(model_aapl.da, 'AAPL', model.tentaif_aapl), 
  model_terbaik(model_amzn.da, 'AMZN', model.tentaif_amzn), 
  model_terbaik(model_goog.da, 'GOOG', model.tentaif_goog),  
  model_terbaik(model_meta.da, 'META', model.tentaif_meta), 
  model_terbaik(model_msft.da, 'MSFT', model.tentaif_msft), 
  model_terbaik(model_nflx.da, 'NFLX', model.tentaif_nflx), 
  model_terbaik(model_nvda.da, 'NVDA', model.tentaif_nvda)  
) %>%
  mutate(across(where(is.numeric), ~round(., 2)))

datatable(tentatif, filter = 'top', 
          options = list(pageLength = 7))

Dari tabel diatas terlihat bahwa model ARIMA(0,1,1) Merupakan model terbaik yang paling sering digunakan.

5 Model ARIMA dengan Drift

Tren menentukan bagaimana perubahan deret waktu dari waktu ke waktu. Dalam model ARIMA, ini dapat dimodelkan menggunakan persamaan dasar berikut [1]. “a” disebut “intercept” dan “b” disebut “drift”. “Drift” adalah hanyalah kemiringan garis lurus. Selanjutnya akan dilakukan kombinasi model ARIMA dengan Intercept dan Drift.

5.1 1. Model Tanpa Intercept dan Tanpa Drift

Simpelnya ini merupakan model data train yang diberi differencing. Yakni memodelkan data train dengan ordo I dari ARIMA(p, d, q) sama dengan 1, sehingga kan membentuk model ARIMA(p, 1, q).

5.1.1 AAPL

drift1_aapl <- 
  model_tentatif(train_aapl.ts, p_max = 6, d = 1, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 x x x x x x x x x x x  x  x  x 
## 1 o o o o o o o o o o o  o  x  x 
## 2 x o o o o o o o o o o  o  o  o 
## 3 x o o o o o o o o o o  o  o  o 
## 4 o x o o o o o o o o o  o  o  o 
## 5 x x x x o o o o o o o  o  o  o 
## 6 x x x x o o o o o o o  o  o  o 
## 7 x x o x x x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## Series: data 
## ARIMA(4,1,4) 
## 
## Coefficients:
##           ar1      ar2      ar3      ar4     ma1     ma2     ma3     ma4
##       -0.1249  -1.2837  -0.1404  -0.9539  0.1613  1.2799  0.1798  0.9628
## s.e.   0.0309   0.0253   0.0272   0.0230  0.0304  0.0211  0.0210  0.0296
## 
## sigma^2 = 4.567:  log likelihood = -1813.48
## AIC=3644.95   AICc=3645.17   BIC=3687.49
datatable(drift1_aapl, filter = 'top', 
          options = list(pageLength = 5))

Berdasarkan pendugaan parameter di atas, nilai AIC terkecil \(3644.95\) dimiliki oleh model ARIMA(4,1,4) dan seluruh parameternya signifikan sehingga model yang dipilih adalah model ARIMA(4,1,4).

model_aapl.drift1 <- Arima(train_aapl.ts, order=c(4,1,4), method="ML")

5.1.2 AMZN

drift1_amzn <- 
  model_tentatif(train_amzn.ts, p_max = 6, d = 1, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 x x x x x x x x x x x  x  x  x 
## 1 o o o o o x o o o o o  o  o  o 
## 2 x o o o o o o o o o o  o  o  o 
## 3 o o o o o o o o o o o  o  o  o 
## 4 x o o o o o o o o o o  o  o  o 
## 5 x o o x o o o o o o o  o  o  o 
## 6 x o o x x o o o o o o  o  o  o 
## 7 x x x x x o o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift1_amzn, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model Tanpa Intercept dan Tanpa Drift pada AMZN tidak ada.

model_amzn.drift1 <- NA

5.1.3 GOOG

drift1_goog <- 
  model_tentatif(train_goog.ts, p_max = 6, d = 1, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 x x x x x x x x x x x  x  x  x 
## 1 o o o o o o o o o o o  o  x  o 
## 2 x o o o o o o o o o o  o  o  o 
## 3 x x o o o o o o o o o  o  o  o 
## 4 x x x o o o o o o o o  o  o  o 
## 5 x x x o o o o o o o o  o  o  o 
## 6 x o x x x o o o o o o  o  o  o 
## 7 x o x x x x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## Series: data 
## ARIMA(2,1,2) 
## 
## Coefficients:
##          ar1      ar2      ma1     ma2
##       0.8095  -0.7623  -0.7708  0.7355
## s.e.  0.3559   0.3009   0.3753  0.3031
## 
## sigma^2 = 3.293:  log likelihood = -1805.32
## AIC=3620.64   AICc=3620.71   BIC=3644.64
datatable(drift1_goog, filter = 'top', 
          options = list(pageLength = 5))

Berdasarkan pendugaan parameter di atas, nilai AIC terkecil \(3620.64\) dimiliki oleh model ARIMA(2,1,2) dan seluruh parameternya signifikan sehingga model yang dipilih adalah model ARIMA(2,1,2).

model_goog.drift1 <- Arima(train_goog.ts, order=c(2,1,2), method="ML")

5.1.4 META

drift1_meta <- 
  model_tentatif(train_meta.ts, p_max = 6, d = 1, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 x x x x x x x x x x x  x  x  x 
## 1 o o o o o o o o o o o  o  o  o 
## 2 x o o o o o o o o o o  o  o  o 
## 3 x x o o o o o o o o o  o  o  o 
## 4 x x o o o o o o o o o  o  o  o 
## 5 x x o o o o o o o o o  o  o  o 
## 6 o x o x o o o o o o o  o  o  o 
## 7 x x x x x o o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift1_meta, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model Tanpa Intercept dan Tanpa Drift pada META tidak ada.

model_meta.drift1 <- NA

5.1.5 MSFT

drift1_msft <- 
  model_tentatif(train_msft.ts, p_max = 6, d = 1, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 x x x x x x x x x x x  x  x  x 
## 1 o o o o x o o o o o o  o  o  o 
## 2 x o o o o x o o o o o  o  o  o 
## 3 x o o o o o o o o o o  o  o  o 
## 4 o x o o o o o o o o o  o  o  o 
## 5 x x x x o o o o o o o  o  o  o 
## 6 x x x x x o o o o o o  o  o  o 
## 7 x x x x x x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift1_msft, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model Tanpa Intercept dan Tanpa Drift pada MSFT tidak ada.

model_msft.drift1 <- NA

5.1.6 NFLX

drift1_nflx <- 
  model_tentatif(train_nflx.ts, p_max = 6, d = 1, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 x x x x x x x x x x x  x  x  x 
## 1 o o o o o o o o o o o  o  x  o 
## 2 x o o o o o o o o o o  o  x  o 
## 3 x x o o o o o o o o o  o  x  o 
## 4 x x x o o o o o o o o  o  x  o 
## 5 x x x x o o o o o o o  o  o  o 
## 6 x x x x o o o o o o o  o  o  o 
## 7 x x x x x x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## Series: data 
## ARIMA(6,1,6) 
## 
## Coefficients:
##           ar1      ar2     ar3      ar4      ar5      ar6     ma1     ma2
##       -0.0661  -0.1421  0.2227  -0.1349  -0.0453  -0.9140  0.1024  0.1549
## s.e.   0.0236   0.0217  0.0217   0.0191   0.0180   0.0298  0.0217  0.0153
##           ma3     ma4     ma5     ma6
##       -0.2166  0.1513  0.1271  0.9798
## s.e.   0.0124  0.0135  0.0201  0.0271
## 
## sigma^2 = 92.54:  log likelihood = -3069.58
## AIC=6165.15   AICc=6165.6   BIC=6226.6
datatable(drift1_nflx, filter = 'top', 
          options = list(pageLength = 5))

Berdasarkan pendugaan parameter di atas, nilai AIC terkecil \(6165.15\) dimiliki oleh model ARIMA(6,1,6) dan seluruh parameternya signifikan sehingga model yang dipilih adalah model ARIMA(6,1,6).

model_nflx.drift1 <- Arima(train_nflx.ts, order=c(6,1,6), method="ML")

5.1.7 NVDA

drift1_nvda <- 
  model_tentatif(train_nvda.ts, p_max = 6, d = 1, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 x x x x x x x x x x x  x  x  x 
## 1 o o o o o o o o o o o  o  o  o 
## 2 x o o o o o o o o o o  o  o  o 
## 3 x x o o o o o o o o o  o  o  o 
## 4 x x x o o o o o o o o  o  o  o 
## 5 x x x o o o o o o o o  o  o  o 
## 6 x x o x x o o o o o o  o  o  o 
## 7 x x x x x x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## Series: data 
## ARIMA(4,1,4) 
## 
## Coefficients:
##          ar1      ar2     ar3      ar4      ma1     ma2      ma3     ma4
##       0.4896  -0.7909  0.5089  -0.9741  -0.4679  0.7750  -0.5034  0.9584
## s.e.  0.0331   0.0170  0.0113   0.0407   0.0481  0.0256   0.0184  0.0520
## 
## sigma^2 = 29.14:  log likelihood = -2522.76
## AIC=5063.53   AICc=5063.75   BIC=5105.84
datatable(drift1_nvda, filter = 'top', 
          options = list(pageLength = 5))

Berdasarkan pendugaan parameter di atas, nilai AIC terkecil \(5063.53\) dimiliki oleh model ARIMA(4,1,4) dan seluruh parameternya signifikan sehingga model yang dipilih adalah model ARIMA(4,1,4).

model_nvda.drift1 <- Arima(train_nvda.ts, order=c(4,1,4), method="ML")

5.1.8 Model Terbaik

drift1 <- rbind(
  model_terbaik(model_aapl.drift1, 'AAPL', drift1_aapl),
  model_terbaik(model_amzn.drift1, 'AMZN', drift1_amzn),
  model_terbaik(model_goog.drift1, 'GOOG', drift1_goog),
  model_terbaik(model_meta.drift1, 'META', drift1_meta),
  model_terbaik(model_msft.drift1, 'MSFT', drift1_msft),
  model_terbaik(model_nflx.drift1, 'NFLX', drift1_nflx),
  model_terbaik(model_nvda.drift1, 'NVDA', drift1_nvda)
) %>%
  mutate(across(where(is.numeric), ~round(., 2)))

datatable(drift1, filter = 'top', 
          options = list(pageLength = 7))

Walaupun berlabel model terbaik, namun terdapat dua model yang tidak semua parameternya signifikan. Yakni Model ARIMA(3,1,3) dari GOOG dan ARIMA(3,1,3) dari META.

5.2 2. Model Dengan Intercept Tanpa Drift

Simpelnya ini merupakan model data differencing tanpa diberi differencing. Yakni memodelkan data differencing dengan ordo I dari ARIMA(p, d, q) sama dengan 0, sehingga kan membentuk model ARIMA(p, 0, q).

5.2.1 AAPL

drift2_aapl <- 
  model_tentatif(train_aapl.diff, p_max =6, d = 0, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o o o o o o o o  o  x  x 
## 1 x o o o o o o o o o o  o  o  x 
## 2 x o o o o o o o o o o  o  o  o 
## 3 x o o o o o o o o o o  o  o  o 
## 4 x o o x o o o o o o o  o  o  o 
## 5 x x x o o o o o o o o  o  o  o 
## 6 x x x x o x o o o o o  o  o  o 
## 7 x o x x x o o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift2_aapl, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model Dengan Intercept Tanpa Drift pada AAPL tidak ada.

model_aapl.drift2 <- NA

5.2.2 AMZN

drift2_amzn <- 
  model_tentatif(train_amzn.diff, p_max = 6, d = 0, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o o x o o o o o  o  o  o 
## 1 x o o o o o o o o o o  o  o  o 
## 2 o o o o o o o o o o o  o  o  o 
## 3 x o o o o o o o o o o  o  o  o 
## 4 x o o x o o o o o o o  o  o  o 
## 5 x x o x x o o o o o o  o  o  o 
## 6 x x x x x o o o o o o  o  o  o 
## 7 x x o o x x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift2_amzn, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model Dengan Intercept Tanpa Drift pada AMZN tidak ada.

model_amzn.drift2 <- NA

5.2.3 GOOG

drift2_goog <- 
  model_tentatif(train_goog.diff, p_max = 6, d = 0, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o o o o o o o o  o  x  o 
## 1 x o o o o o o o o o o  o  o  o 
## 2 x x o o o o o o o o o  o  o  o 
## 3 x x x o o o o o o o o  o  o  o 
## 4 x x x o o o o o o o o  o  o  o 
## 5 x x x x x o o o o o o  o  o  o 
## 6 x o x x x x o o o o o  o  o  o 
## 7 x x x x x o o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift2_goog, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model Dengan Intercept Tanpa Drift pada GOOG tidak ada.

model_goog.drift2 <- NA

5.2.4 META

drift2_meta <- 
  model_tentatif(train_meta.diff, p_max = 6, d = 0, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o o o o o o o o  o  o  o 
## 1 x o o o o o o o o o o  o  o  o 
## 2 x o o o o o o o o o o  o  o  o 
## 3 o x o o o o o o o o o  o  o  o 
## 4 o x x x o o o o o o o  o  o  o 
## 5 x x o x x o o o o o o  o  o  o 
## 6 x x o x x x o o o o o  o  o  o 
## 7 x x x x x x x o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift2_meta, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model Dengan Intercept Tanpa Drift pada META tidak ada.

model_meta.drift2 <- NA

5.2.5 MSFT

drift2_msft <- 
  model_tentatif(train_msft.diff, p_max = 6, d = 0, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o x o o o o o o  o  o  x 
## 1 x o o o o x o o o o o  o  o  x 
## 2 x x o o o o o o o o o  o  o  o 
## 3 x o x o o o o o o o o  o  o  o 
## 4 x o x x o o o o o o o  o  o  o 
## 5 x x x x x o o o o o o  o  o  o 
## 6 x x x x x x o o o o o  o  o  o 
## 7 x x x o o x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift2_msft, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model Dengan Intercept Tanpa Drift pada MSFT tidak ada.

model_msft.drift2 <- NA

5.2.6 NFLX

drift2_nflx <- 
  model_tentatif(train_nflx.diff, p_max = 6, d = 0, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o o o o o o o o  o  x  o 
## 1 x o o o o o o o o o o  o  x  o 
## 2 x x o o o o o o o o o  o  x  o 
## 3 x x x o o o o o o o o  o  x  o 
## 4 x x x x o o o o o o o  o  o  o 
## 5 x x x x o o o o o o o  o  o  o 
## 6 x x o x x x o o o o o  o  o  o 
## 7 x x x x x x x o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift2_nflx, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model Dengan Intercept Tanpa Drift pada NFLX tidak ada.

model_nflx.drift2 <- NA

5.2.7 NVDA

drift2_nvda <- 
  model_tentatif(train_nvda.diff, p_max = 6, d = 0, q_max = 12)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o o o o o o o o  o  o  o 
## 1 x o o o o o o o o o o  o  o  o 
## 2 x x o o o o o o o o o  o  o  o 
## 3 x o x o o o o o o o o  o  o  o 
## 4 o x x x o o o o o o o  o  o  o 
## 5 x x x o x o o o o o o  o  o  o 
## 6 x x x x x x o o o o o  o  o  o 
## 7 x x o x x x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift2_nvda, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model Dengan Intercept Tanpa Drift pada NVDA tidak ada.

model_nvda.drift2 <- NA

5.2.8 Model Terbaik

drift2 <- rbind(
  model_terbaik(model_aapl.drift2, 'AAPL', drift2_aapl),
  model_terbaik(model_amzn.drift2, 'AMZN', drift2_amzn),
  model_terbaik(model_goog.drift2, 'GOOG', drift2_goog),
  model_terbaik(model_meta.drift2, 'META', drift2_meta),
  model_terbaik(model_msft.drift2, 'MSFT', drift2_msft),
  model_terbaik(model_nflx.drift2, 'NFLX', drift2_nflx),
  model_terbaik(model_nvda.drift2, 'NVDA', drift2_nvda)
) %>%
  mutate(across(where(is.numeric), ~round(., 2)))

datatable(drift2, filter = 'top', 
          options = list(pageLength = 7))

Walaupun berlabel model terbaik, namun tidak ada satupun yang semua parameternya signifikan.

5.3 3. Model Tanpa Intercept Dengan Drift

Simpelnya ini merupakan model data train yang diberi differencing dan drift. Yakni memodelkan data train dengan ordo I dari ARIMA(p, d, q) sama dengan 1, sehingga kan membentuk model ARIMA(p, 1, q) + include.drift.

5.3.1 AAPL

drift3_aapl <- 
  model_tentatif(train_aapl.ts, p_max = 6, d = 1, q_max = 12, drift=TRUE)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 x x x x x x x x x x x  x  x  x 
## 1 o o o o o o o o o o o  o  x  x 
## 2 x o o o o o o o o o o  o  o  o 
## 3 x o o o o o o o o o o  o  o  o 
## 4 o x o o o o o o o o o  o  o  o 
## 5 x x x x o o o o o o o  o  o  o 
## 6 x x x x o o o o o o o  o  o  o 
## 7 x x o x x x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift3_aapl, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model Tanpa Intercept Dengan Drift pada AAPL tidak ada.

model_aapl.drift3 <- NA

5.3.2 AMZN

drift3_amzn <- 
  model_tentatif(train_amzn.ts, p_max = 6, d = 1, q_max = 12, drift=TRUE)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 x x x x x x x x x x x  x  x  x 
## 1 o o o o o x o o o o o  o  o  o 
## 2 x o o o o o o o o o o  o  o  o 
## 3 o o o o o o o o o o o  o  o  o 
## 4 x o o o o o o o o o o  o  o  o 
## 5 x o o x o o o o o o o  o  o  o 
## 6 x o o x x o o o o o o  o  o  o 
## 7 x x x x x o o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift3_amzn, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model Tanpa Intercept Dengan Drift pada AMZN tidak ada.

model_amzn.drift3 <- NA

5.3.3 GOOG

drift3_goog <- 
  model_tentatif(train_goog.ts, p_max = 6, d = 1, q_max = 12, drift=TRUE)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 x x x x x x x x x x x  x  x  x 
## 1 o o o o o o o o o o o  o  x  o 
## 2 x o o o o o o o o o o  o  o  o 
## 3 x x o o o o o o o o o  o  o  o 
## 4 x x x o o o o o o o o  o  o  o 
## 5 x x x o o o o o o o o  o  o  o 
## 6 x o x x x o o o o o o  o  o  o 
## 7 x o x x x x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift3_goog, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model Tanpa Intercept Dengan Drift pada GOOG tidak ada.

model_goog.drift3 <- NA

5.3.4 META

drift3_meta <- 
  model_tentatif(train_meta.ts, p_max = 6, d = 1, q_max = 12, drift=TRUE)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 x x x x x x x x x x x  x  x  x 
## 1 o o o o o o o o o o o  o  o  o 
## 2 x o o o o o o o o o o  o  o  o 
## 3 x x o o o o o o o o o  o  o  o 
## 4 x x o o o o o o o o o  o  o  o 
## 5 x x o o o o o o o o o  o  o  o 
## 6 o x o x o o o o o o o  o  o  o 
## 7 x x x x x o o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift3_meta, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model Tanpa Intercept Dengan Drift pada META tidak ada.

model_meta.drift3 <- NA

5.3.5 MSFT

drift3_msft <- 
  model_tentatif(train_msft.ts, p_max = 6, d = 1, q_max = 12, drift=TRUE)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 x x x x x x x x x x x  x  x  x 
## 1 o o o o x o o o o o o  o  o  o 
## 2 x o o o o x o o o o o  o  o  o 
## 3 x o o o o o o o o o o  o  o  o 
## 4 o x o o o o o o o o o  o  o  o 
## 5 x x x x o o o o o o o  o  o  o 
## 6 x x x x x o o o o o o  o  o  o 
## 7 x x x x x x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift3_msft, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model Tanpa Intercept Dengan Drift pada MSFT tidak ada.

model_msft.drift3 <- NA

5.3.6 NFLX

drift3_nflx <- 
  model_tentatif(train_nflx.ts, p_max = 6, d = 1, q_max = 12, drift=TRUE)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 x x x x x x x x x x x  x  x  x 
## 1 o o o o o o o o o o o  o  x  o 
## 2 x o o o o o o o o o o  o  x  o 
## 3 x x o o o o o o o o o  o  x  o 
## 4 x x x o o o o o o o o  o  x  o 
## 5 x x x x o o o o o o o  o  o  o 
## 6 x x x x o o o o o o o  o  o  o 
## 7 x x x x x x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift3_nflx, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model Tanpa Intercept Dengan Drift pada NFLX tidak ada.

model_nflx.drift3 <- NA

5.3.7 NVDA

drift3_nvda <- 
  model_tentatif(train_nvda.ts, p_max = 6, d = 1, q_max = 12, drift=TRUE)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 x x x x x x x x x x x  x  x  x 
## 1 o o o o o o o o o o o  o  o  o 
## 2 x o o o o o o o o o o  o  o  o 
## 3 x x o o o o o o o o o  o  o  o 
## 4 x x x o o o o o o o o  o  o  o 
## 5 x x x o o o o o o o o  o  o  o 
## 6 x x o x x o o o o o o  o  o  o 
## 7 x x x x x x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift3_nvda, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model Tanpa Intercept Dengan Drift pada NVDA tidak ada.

model_nvda.drift3 <- NA

5.3.8 Model Terbaik

drift3 <- rbind(
  model_terbaik(model_aapl.drift3, 'AAPL', drift3_aapl),
  model_terbaik(model_amzn.drift3, 'AMZN', drift3_amzn),
  model_terbaik(model_goog.drift3, 'GOOG', drift3_goog),
  model_terbaik(model_meta.drift3, 'META', drift3_meta),
  model_terbaik(model_msft.drift3, 'MSFT', drift3_msft),
  model_terbaik(model_nflx.drift3, 'NFLX', drift3_nflx),
  model_terbaik(model_nvda.drift3, 'NVDA', drift3_nvda)
) %>%
  mutate(across(where(is.numeric), ~round(., 2)))

datatable(drift3, filter = 'top', 
          options = list(pageLength = 7))

Walaupun berlabel model terbaik, namun tidak ada satupun yang semua parameternya signifikan.

5.4 4. Model dengan Intercept dan dengan Drift

Simpelnya ini merupakan model data differencing tanpa diberi differencing namun diberi drift. Yakni memodelkan data differencing dengan ordo I dari ARIMA(p, d, q) sama dengan 0, sehingga kan membentuk model ARIMA(p, 0, q) + include.drift.

5.4.1 AAPL

drift4_aapl <- 
  model_tentatif(train_aapl.diff, p_max = 6, d = 0, q_max = 12, drift=TRUE)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o o o o o o o o  o  x  x 
## 1 x o o o o o o o o o o  o  o  x 
## 2 x o o o o o o o o o o  o  o  o 
## 3 x o o o o o o o o o o  o  o  o 
## 4 x o o x o o o o o o o  o  o  o 
## 5 x x x o o o o o o o o  o  o  o 
## 6 x x x x o x o o o o o  o  o  o 
## 7 x o x x x o o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift4_aapl, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model dengan Intercept dan dengan Drift pada AAPL tidak ada.

model_aapl.drift4 <- NA

5.4.2 AMZN

drift4_amzn <- 
  model_tentatif(train_amzn.diff, p_max = 6, d = 0, q_max = 12, drift=TRUE)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o o x o o o o o  o  o  o 
## 1 x o o o o o o o o o o  o  o  o 
## 2 o o o o o o o o o o o  o  o  o 
## 3 x o o o o o o o o o o  o  o  o 
## 4 x o o x o o o o o o o  o  o  o 
## 5 x x o x x o o o o o o  o  o  o 
## 6 x x x x x o o o o o o  o  o  o 
## 7 x x o o x x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift4_amzn, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model dengan Intercept dan dengan Drift pada AMZN tidak ada.

model_amzn.drift4 <- NA

5.4.3 GOOG

drift4_goog <- 
  model_tentatif(train_goog.diff, p_max = 6, d = 0, q_max = 12, drift=TRUE)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o o o o o o o o  o  x  o 
## 1 x o o o o o o o o o o  o  o  o 
## 2 x x o o o o o o o o o  o  o  o 
## 3 x x x o o o o o o o o  o  o  o 
## 4 x x x o o o o o o o o  o  o  o 
## 5 x x x x x o o o o o o  o  o  o 
## 6 x o x x x x o o o o o  o  o  o 
## 7 x x x x x o o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift4_goog, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model dengan Intercept dan dengan Drift pada GOOG tidak ada.

model_goog.drift4 <- NA

5.4.4 META

drift4_meta <- 
  model_tentatif(train_meta.diff, p_max = 6, d = 0, q_max = 12, drift=TRUE)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o o o o o o o o  o  o  o 
## 1 x o o o o o o o o o o  o  o  o 
## 2 x o o o o o o o o o o  o  o  o 
## 3 o x o o o o o o o o o  o  o  o 
## 4 o x x x o o o o o o o  o  o  o 
## 5 x x o x x o o o o o o  o  o  o 
## 6 x x o x x x o o o o o  o  o  o 
## 7 x x x x x x x o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift4_meta, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model dengan Intercept dan dengan Drift pada AMETA tidak ada.

model_meta.drift4 <- NA

5.4.5 MSFT

drift4_msft <- 
  model_tentatif(train_msft.diff, p_max = 6, d = 0, q_max = 12, drift=TRUE)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o x o o o o o o  o  o  x 
## 1 x o o o o x o o o o o  o  o  x 
## 2 x x o o o o o o o o o  o  o  o 
## 3 x o x o o o o o o o o  o  o  o 
## 4 x o x x o o o o o o o  o  o  o 
## 5 x x x x x o o o o o o  o  o  o 
## 6 x x x x x x o o o o o  o  o  o 
## 7 x x x o o x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift4_msft, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model dengan Intercept dan dengan Drift pada MSFT tidak ada.

model_msft.drift4 <- NA

5.4.6 NFLX

drift4_nflx <- 
  model_tentatif(train_nflx.diff, p_max = 6, d = 0, q_max = 12, drift=TRUE)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o o o o o o o o  o  x  o 
## 1 x o o o o o o o o o o  o  x  o 
## 2 x x o o o o o o o o o  o  x  o 
## 3 x x x o o o o o o o o  o  x  o 
## 4 x x x x o o o o o o o  o  o  o 
## 5 x x x x o o o o o o o  o  o  o 
## 6 x x o x x x o o o o o  o  o  o 
## 7 x x x x x x x o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift4_nflx, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model dengan Intercept dan dengan Drift pada NFLX tidak ada.

model_nflx.drift4 <- NA

5.4.7 NVDA

drift4_nvda <- 
  model_tentatif(train_nvda.diff, p_max = 6, d = 1, q_max = 12, drift=TRUE)
## AR/MA
##   0 1 2 3 4 5 6 7 8 9 10 11 12 13
## 0 o o o o o o o o o o o  o  o  o 
## 1 x o o o o o o o o o o  o  o  o 
## 2 x x o o o o o o o o o  o  o  o 
## 3 x o x o o o o o o o o  o  o  o 
## 4 o x x x o o o o o o o  o  o  o 
## 5 x x x o x o o o o o o  o  o  o 
## 6 x x x x x x o o o o o  o  o  o 
## 7 x x o x x x o o o o o  o  o  o 
## 
## Model ARIMA dengan AIC terkecil:
## NULL
datatable(drift4_nvda, filter = 'top', 
          options = list(pageLength = 5))

Kategori Model terbaik tidak ada yang terpenuhi, yakni tidak ada nilai AIC terkecil dengan semua parameter nya signifikan dan tidak ada NA di dalam nya. Sehingga Model dengan Intercept dan dengan Drift pada NVDA tidak ada.

model_nvda.drift4 <- NA

5.4.8 Model Terbaik

drift4 <- rbind(
  model_terbaik(model_aapl.drift4, 'AAPL', drift4_aapl),
  model_terbaik(model_amzn.drift4, 'AMZN', drift4_amzn),
  model_terbaik(model_goog.drift4, 'GOOG', drift4_goog),
  model_terbaik(model_meta.drift4, 'META', drift4_meta),
  model_terbaik(model_msft.drift4, 'MSFT', drift4_msft),
  model_terbaik(model_nflx.drift4, 'NFLX', drift4_nflx),
  model_terbaik(model_nvda.drift4, 'NVDA', drift4_nvda)
) %>%
  mutate(across(where(is.numeric), ~round(., 2)))

datatable(drift4, filter = 'top', 
          options = list(pageLength = 7))

Walaupun berlabel model terbaik, namun tidak ada satupun yang semua parameternya signifikan.

5.5 Kesimpulan

Jika dibandingkan dengan model tentatif terbaik sebelumnya. Maka terlihat bahwa model itu jauh lebih baik. Sehingga model dengan kombinasi drift tidak dipilih.

6 Analisis Sisaan

Model terbaik hasil identifikasi kemudian dicek asumsi sisaannya. Sisaan model ARIMA harus memenuhi asumsi normalitas, kebebasan sisaan, dan kehomogenan ragam. Diagnostik model dilakukan secara eksplorasi dan uji formal.

6.1 Eksplorasi Sisaan

6.1.1 AAPL

#Eksplorasi 
sisaan_aapl.da <- model_aapl.da$residuals 
par(mfrow=c(2,2)) 
# QQ-Plot
qqnorm(sisaan_aapl.da) 
qqline(sisaan_aapl.da, col = "dodgerblue3", lwd = 2.5) 
# Plot sisaan
plot(c(1:length(sisaan_aapl.da)), sisaan_aapl.da, 
     main = "Sisaan Model ARIMA",
     xlab = "Periode",
     ylab = "Nilai Sisaan")
abline(h = 0, col = "firebrick2", lty = 2, lwd=2.5)
#Plot ACF dan PACF
stats::acf(sisaan_aapl.da, ylim=c(-.1,.1)) 
stats::pacf(sisaan_aapl.da, ylim=c(-.1,.1)) 

Berdasarkan plot kuantil-kuantil normal, secara eksplorasi ditunjukkan sisaan tidak menyebar normal ditandai dengan tititk-titiknya cenderung tidak mengikuti garis \(45^{\circ}\). Kemudian dapat dilihat juga lebar pita sisaan yang cenderung berbeda menandakan bahwa sisaan memiliki ragam yang tidak homogen. Plot ACF dan PACF sisaan ARIMA(0,1,1) juga signifikan pada 20 lag awal yang menandakan tidak saling bebas. Kondisi ini akan diuji lebih lanjut dengan uji formal.

6.1.2 AMZN

#Eksplorasi 
sisaan_amzn.da <- model_amzn.da$residuals 
par(mfrow=c(2,2)) 
# QQ-Plot
qqnorm(sisaan_amzn.da) 
qqline(sisaan_amzn.da, col = "dodgerblue3", lwd = 2.5) 
# Plot sisaan
plot(c(1:length(sisaan_amzn.da)), sisaan_amzn.da, 
     main = "Plot Sisaan Model ARIMA",
     xlab = "Periode",
     ylab = "Nilai Sisaan")
abline(h = 0, col = "firebrick2", lty = 2, lwd=2.5)
#Plot ACF dan PACF
stats::acf(sisaan_amzn.da, ylim=c(-.1,.1)) 
stats::pacf(sisaan_amzn.da, ylim=c(-.1,.1)) 

Berdasarkan plot kuantil-kuantil normal, secara eksplorasi ditunjukkan sisaan tidak menyebar normal ditandai dengan titik-titik yang cenderung tidak mengikuti garis \(45^{\circ}\). Kemudian dapat dilihat juga lebar pita sisaan yang cenderung berbeda menandakan bahwa sisaan memiliki ragam yang tidak homogen. Plot ACF dan PACF sisaan ARIMA(0,1,1) juga signifikan pada 20 lag awal yang menandakan tidak saling bebas. Kondisi ini akan diuji lebih lanjut dengan uji formal.

6.1.3 GOOG

#Eksplorasi 
sisaan_goog.da <- model_goog.da$residuals 
par(mfrow=c(2,2)) 
# QQ-Plot
qqnorm(sisaan_goog.da) 
qqline(sisaan_goog.da, col = "dodgerblue3", lwd = 2.5) 
# Plot sisaan
plot(c(1:length(sisaan_goog.da)), sisaan_goog.da, 
     main = "Plot Sisaan Model ARIMA",
     xlab = "Periode",
     ylab = "Nilai Sisaan")
abline(h = 0, col = "firebrick2", lty = 2, lwd=2.5)
#Plot ACF dan PACF
stats::acf(sisaan_goog.da, ylim=c(-.1,.1)) 
stats::pacf(sisaan_goog.da, ylim=c(-.1,.1)) 

Berdasarkan plot kuantil-kuantil normal, secara eksplorasi ditunjukkan sisaan tidak menyebar normal ditandai dengan titik titik yang cenderung mengikuti garis \(45^{\circ}\). Kemudian dapat dilihat juga lebar pita sisaan yang cenderung tidak sama menandakan bahwa sisaan memiliki ragam yang tidak homogen. Plot ACF dan PACF sisaan ARIMA(4,1,5) juga signifikan pada 20 lag awal yang menandakan tidak saling bebas. Kondisi ini akan diuji lebih lanjut dengan uji formal.

6.1.4 META

#Eksplorasi 
sisaan_meta.da <- model_meta.da$residuals 
par(mfrow=c(2,2)) 
# QQ-Plot
qqnorm(sisaan_meta.da) 
qqline(sisaan_meta.da, col = "dodgerblue3", lwd = 2.5) 
# Plot sisaan
plot(c(1:length(sisaan_meta.da)), sisaan_meta.da, 
     main = "Plot Sisaan Model ARIMA",
     xlab = "Periode",
     ylab = "Nilai Sisaan")
abline(h = 0, col = "firebrick2", lty = 2, lwd=2.5)
#Plot ACF dan PACF
stats::acf(sisaan_meta.da, ylim=c(-.1,.1)) 
stats::pacf(sisaan_meta.da, ylim=c(-.1,.1)) 

Berdasarkan plot kuantil-kuantil normal, secara eksplorasi ditunjukkan sisaan tidak menyebar normal ditandai dengan tidak semua titik (ada pencilan) mengikuti garis \(45^{\circ}\). Kemudian dapat dilihat juga lebar pita sisaan yang cenderung tidak sama dikarenakan adanya pencilan menandakan bahwa sisaan memiliki ragam yang tidak homogen. Plot ACF dan PACF sisaan ARIMA(0,1,1) juga signifikan pada 20 lag awal yang menandakan tidak saling bebas. Kondisi ini akan diuji lebih lanjut dengan uji formal.

6.1.5 MSFT

#Eksplorasi 
sisaan_msft.da <- model_msft.da$residuals 
par(mfrow=c(2,2)) 
# QQ-Plot
qqnorm(sisaan_msft.da) 
qqline(sisaan_msft.da, col = "dodgerblue3", lwd = 2.5) 
# Plot sisaan
plot(c(1:length(sisaan_msft.da)), sisaan_msft.da, 
     main = "Plot Sisaan Model ARIMA",
     xlab = "Periode",
     ylab = "Nilai Sisaan")
abline(h = 0, col = "firebrick2", lty = 2, lwd=2.5)
#Plot ACF dan PACF
stats::acf(sisaan_msft.da, ylim=c(-.1,.1)) 
stats::pacf(sisaan_msft.da, ylim=c(-.1,.1)) 

Berdasarkan plot kuantil-kuantil normal, secara eksplorasi ditunjukkan sisaan menyebar tidak normal ditandai dengan titik titik yang cenderung tidak mengikuti garis \(45^{\circ}\). Kemudian dapat dilihat juga lebar pita sisaan yang cenderung sama menandakan bahwa sisaan memiliki ragam yang homogen. Plot ACF dan PACF sisaan ARIMA(4,1,5) juga signifikan pada 20 lag awal yang menandakan tidak saling bebas. Kondisi ini akan diuji lebih lanjut dengan uji formal.

6.1.6 NFLX

#Eksplorasi 
sisaan_nflx.da <- model_nflx.da$residuals 
par(mfrow=c(2,2)) 
# QQ-Plot
qqnorm(sisaan_nflx.da) 
qqline(sisaan_nflx.da, col = "dodgerblue3", lwd = 2.5) 
# Plot sisaan
plot(c(1:length(sisaan_nflx.da)), sisaan_nflx.da, 
     main = "Plot Sisaan Model ARIMA",
     xlab = "Periode",
     ylab = "Nilai Sisaan")
abline(h = 0, col = "firebrick2", lty = 2, lwd=2.5)
#Plot ACF dan PACF
stats::acf(sisaan_nflx.da, ylim=c(-.1,.1)) 
stats::pacf(sisaan_nflx.da, ylim=c(-.1,.1)) 

Berdasarkan plot kuantil-kuantil normal, secara eksplorasi ditunjukkan sisaan tidak menyebar normal ditandai dengan tidak semua titik (ada pencilan) mengikuti garis \(45^{\circ}\). Kemudian dapat dilihat juga lebar pita sisaan yang cenderung tidak sama dikarenakan adanya pencilan menandakan bahwa sisaan memiliki ragam yang tidak homogen. Plot ACF dan PACF sisaan ARIMA(6,1,7) juga signifikan pada 20 lag awal yang menandakan tidak saling bebas. Kondisi ini akan diuji lebih lanjut dengan uji formal.

6.1.7 NVDA

#Eksplorasi 
sisaan_nvda.da <- model_nvda.da$residuals 
par(mfrow=c(2,2)) 
# QQ-Plot
qqnorm(sisaan_nvda.da) 
qqline(sisaan_nvda.da, col = "dodgerblue3", lwd = 2.5) 
# Plot sisaan
plot(c(1:length(sisaan_nvda.da)), sisaan_nvda.da, 
     main = "Plot Sisaan Model ARIMA",
     xlab = "Periode",
     ylab = "Nilai Sisaan")
abline(h = 0, col = "firebrick2", lty = 2, lwd=2.5)
#Plot ACF dan PACF
stats::acf(sisaan_nvda.da, ylim=c(-.1,.1)) 
stats::pacf(sisaan_nvda.da, ylim=c(-.1,.1)) 

Berdasarkan plot kuantil-kuantil normal, secara eksplorasi ditunjukkan sisaan menyebar tidak normal ditandai dengan tititk-titiknya cenderung tidak mengikuti garis \(45^{\circ}\). Kemudian dapat dilihat juga lebar pita sisaan yang cenderung sama menandakan bahwa sisaan memiliki ragam yang homogen. Plot ACF dan PACF sisaan ARIMA(0,1,1) juga tidak signifikan pada 20 lag awal yang menandakan saling bebas. Kondisi ini akan diuji lebih lanjut dengan uji formal.

6.2 Uji Formal

6.2.1 Sisaan Menyebar Normal

Selain dengan eksplorasi, asumsi tersebut dapat diuji menggunakan uji formal. Pada tahapan ini uji formal yang digunakan untuk normalitas adalah uji Kolmogorov-Smirnov (KS). Hipotesis pada uji KS adalah sebagai berikut.

\(H_0\) : Sisaan menyebar normal

\(H_1\) : Sisaan tidak menyebar normal

#tak tolak H0 > sisaan menyebar normal
normal <- data.frame(
  p.value = c(ks.test(sisaan_aapl.da, "pnorm")[["p.value"]],
              ks.test(sisaan_amzn.da, "pnorm")[["p.value"]],
              ks.test(sisaan_goog.da, "pnorm")[["p.value"]],
              ks.test(sisaan_meta.da, "pnorm")[["p.value"]],
              ks.test(sisaan_msft.da, "pnorm")[["p.value"]],
              ks.test(sisaan_nflx.da, "pnorm")[["p.value"]],
              ks.test(sisaan_nvda.da, "pnorm")[["p.value"]]
              )
) 
normal$Keterangan <- ifelse(normal$p.value < 0.05, 
                              "Sisaan Tidak Menyebar Normal", 
                              "Sisaan Menyebar Normal")

normal$p.value <- ifelse(normal$p.value == 0, "< 2.2e-16",
                           sprintf("%.2e", normal$p.value) )

rownames(normal) <- unique(data$Name)
datatable(normal, filter = 'top', 
          options = list(pageLength = 7))

Berdasarkan uji KS tersebut, didapat p-value yang sangat kecil yakni kurang dari \(2.2\times10^{-16}\). Semua p-value nya kurang dari taraf nyata \(5\%\) sehingga tolak \(H_0\) dan menandakan bahwa sisaan tidak menyebar normal. Sesuai dengan qqplot sisaan.

6.2.2 Sisaan saling bebas

Selanjutnya akan dilakukan uji formal untuk kebebasan sisaan menggunakan uji Ljung-Box. Hipotesis yang digunakan adalah sebagai berikut.

\(H_0\) : Sisaan saling bebas

\(H_1\) : Sisaan tidak tidak saling bebas

#tak tolak H0 > sisaan saling bebas
bebas <- data.frame(
  p.value = c(Box.test(sisaan_aapl.da, type = "Ljung")[["p.value"]],
              Box.test(sisaan_amzn.da, type = "Ljung")[["p.value"]],
              Box.test(sisaan_goog.da, type = "Ljung")[["p.value"]],
              Box.test(sisaan_meta.da, type = "Ljung")[["p.value"]],
              Box.test(sisaan_msft.da, type = "Ljung")[["p.value"]],
              Box.test(sisaan_nflx.da, type = "Ljung")[["p.value"]],
              Box.test(sisaan_nvda.da, type = "Ljung")[["p.value"]]
              )
) 
bebas$Keterangan <- ifelse(bebas$p.value < 0.05, 
                              "Sisaan Tidak Saling Bebas", 
                              "Sisaan Saling Bebas")

bebas$p.value <- ifelse(bebas$p.value == 0, "< 2.2e-16",
                           round(bebas$p.value, 2) )

rownames(bebas) <- unique(data$Name)
datatable(bebas, filter = 'top', 
          options = list(pageLength = 7))

Berdasarkan uji Ljung-Box tersebut, semua p-value nya lebih besar dari taraf nyata \(5\%\) sehingga tak tolak \(H_0\) dan menandakan bahwa sisaan saling bebas. Yang berarti tidak ada autokorelasi. Hal ini sesuai dengan hasil eksplorasi menggunakan plot ACF dan PACF.

6.2.3 Sisaan homogen

Hipotesis yang digunakan untuk uji kehomogenan ragam adalah sebagai berikut.

\(H_0\) : Ragam sisaan homogen

\(H_1\) : Ragam sisaan tidak homogen

#tak tolak H0 > sisaan homogen
homogen <- data.frame(
  p.value = c(Box.test((sisaan_aapl.da)^2, type = "Ljung")[["p.value"]],
              Box.test((sisaan_amzn.da)^2, type = "Ljung")[["p.value"]], 
              Box.test((sisaan_goog.da)^2, type = "Ljung")[["p.value"]], 
              Box.test((sisaan_meta.da)^2, type = "Ljung")[["p.value"]], 
              Box.test((sisaan_msft.da)^2, type = "Ljung")[["p.value"]], 
              Box.test((sisaan_nflx.da)^2, type = "Ljung")[["p.value"]], 
              Box.test((sisaan_nvda.da)^2, type = "Ljung")[["p.value"]]
              )
) 
homogen$Keterangan <- ifelse(homogen$p.value < 0.05, 
                              "Sisaan Tidak Homogen", 
                              "Sisaan Homogen")

homogen$p.value <- ifelse(homogen$p.value == 0, "< 2.2e-16",
                           (round(homogen$p.value, 3)) )

homogen$p.value <- ifelse(homogen$p.value == 0, "< 2.2e-16",
                           (round(homogen$p.value, 3)) )

rownames(homogen) <- unique(data$Name)
datatable(homogen, filter = 'top', 
          options = list(pageLength = 7))

Berdasarkan uji Ljung-Box terhadap sisaan kuadrat tersebut, hanya ada 2 yang lebih besar dari dari taraf nyata \(5\%\) yakni META dan NFLX. Yang berarti tak tolak \(H_0\) dan menandakan bahwa ragam sisaan homogen. Sisanya lebih kecil dari taraf nyata \(5\%\) sehingga tolak \(H_0\) dan menandakan bahwa ragam sisaan tidak homogen.

6.2.4 Nilai tengah sama dengan nol

Terakhir, dengan uji-t, akan dicek apakah nilai tengah sisaan sama dengan nol. Hipotesis yang diujikan sebagai berikut.

\(H_0\) : nilai tengah sisaan sama dengan 0

\(H_1\) : nilai tengah sisaan tidak sama dengan 0

#tak tolak h0 > nilai tengah sisaan sama dengan 0 
nol <- data.frame(
  p.value = c(t.test(sisaan_aapl.da, mu=0, conf.level=.95)[["p.value"]],
              t.test(sisaan_amzn.da, mu=0, conf.level=.95)[["p.value"]], 
              t.test(sisaan_goog.da, mu=0, conf.level=.95)[["p.value"]], 
              t.test(sisaan_meta.da, mu=0, conf.level=.95)[["p.value"]], 
              t.test(sisaan_msft.da, mu=0, conf.level=.95)[["p.value"]], 
              t.test(sisaan_nflx.da, mu=0, conf.level=.95)[["p.value"]], 
              t.test(sisaan_nvda.da, mu=0, conf.level=.95)[["p.value"]]
              )
) 
nol$Keterangan <- ifelse(nol$p.value < 0.05, 
                              "Nilai Tengah \u2260 0", 
                              "Nilai Tengah = 0")

nol$p.value <- ifelse(nol$p.value == 0, "< 2.2e-16",
                           round(nol$p.value, 3) )

rownames(nol) <- unique(data$Name)
datatable(nol, filter = 'top', 
          options = list(pageLength = 7))

Berdasarkan uji-t tersebut, semua p-value lebih besar dari taraf nyata \(5\%\) sehingga tak tolak \(H_0\) dan menandakan bahwa nilai tengah sisaan sama dengan nol.

7 Peramalan

Peramalan dilakukan menggunakan fungsi forecast() . Contoh peramalan berikut ini dilakukan untuk \(h\) hari ke depan. Dengan \(h\) adalah banyaknya data test per perusahaan.

#---FORECAST---#
ramalan_aapl.da <- forecast::forecast(model_aapl.da, h = nrow(test_aapl)) 
ramalan_amzn.da <- forecast::forecast(model_amzn.da, h = nrow(test_amzn)) 
ramalan_goog.da <- forecast::forecast(model_goog.da, h = nrow(test_goog)) 
ramalan_meta.da <- forecast::forecast(model_meta.da, h = nrow(test_meta)) 
ramalan_msft.da <- forecast::forecast(model_msft.da, h = nrow(test_msft)) 
ramalan_nflx.da <- forecast::forecast(model_nflx.da, h = nrow(test_nflx)) 
ramalan_nvda.da <- forecast::forecast(model_nvda.da, h = nrow(test_nvda)) 

7.1 Plot Ramalan

data.ramalan_aapl.da <- ramalan_aapl.da$mean
data.ramalan_amzn.da <- ramalan_amzn.da$mean
data.ramalan_goog.da <- ramalan_goog.da$mean
data.ramalan_meta.da <- ramalan_meta.da$mean
data.ramalan_msft.da <- ramalan_msft.da$mean
data.ramalan_nflx.da <- ramalan_nflx.da$mean
data.ramalan_nvda.da <- ramalan_nvda.da$mean

#Plot
par(mfrow=c(2,4))
plot(ramalan_aapl.da, main="AAPL", xlab="Periode(Hari)", cex.main=4, 
     lwd=2.5, col="lightskyblue4")
plot(ramalan_amzn.da, main="AMZN", xlab="Periode(Hari)", cex.main=4, 
     lwd=2.5, col = "orange2")
plot(ramalan_goog.da, main="GOOG", xlab="Periode(Hari)", cex.main=4, 
     lwd=2.5, col = "gold3")
plot(ramalan_meta.da, main="META", xlab="Periode(Hari)", cex.main=4, 
     lwd=2.5, col = "dodgerblue3")
plot(ramalan_msft.da, main="MSFT", xlab="Periode(Hari)", cex.main=4, 
     lwd=2.5, col = "deepskyblue2")
plot(ramalan_nflx.da, main="NFLX", xlab="Periode(Hari)", cex.main=4, 
     lwd=2.5, col = "firebrick3")
plot(ramalan_nvda.da, main="NVDA", xlab="Periode(Hari)", cex.main=4, 
     lwd=2.5, col = "green4")

Berdasarkan hasil plot ramalan di atas, dapat dilihat bahwa semua ramalan cenderung stabil hingga akhir periode.

#Diff inv
ramal_aapl <- diffinv(data.ramalan_aapl.da, differences=1) +
  train_aapl.ts.new[length(train_aapl.ts)]    #nilai akhir data latih

ramal_amzn <- diffinv(data.ramalan_amzn.da, differences=1) +
  train_amzn.ts.new[length(train_amzn.ts)]

ramal_goog <- diffinv(data.ramalan_goog.da, differences=1) +
  train_goog.ts.new[length(train_goog.ts)]

ramal_meta <- diffinv(data.ramalan_meta.da, differences=1) +
  train_meta.ts.new[length(train_meta.ts)]

ramal_msft <- diffinv(data.ramalan_msft.da, differences=1) +
  train_msft.ts.new[length(train_msft.ts)]

ramal_nflx <- diffinv(data.ramalan_nflx.da, differences=1) +
  train_nflx.ts[length(train_nflx.ts)]

ramal_nvda <- diffinv(data.ramalan_nvda.da, differences=1) +
  train_nvda.ts.new[length(train_nvda.ts)]

#Transformasi Balik
ramal_aapl <- 1/(ramal_aapl)^2
ramal_amzn <- (lambda_amzn * (ramal_amzn) + 1)^(1/lambda_amzn)
ramal_goog <- (ramal_goog)^2
ramal_meta <- (lambda_meta * (ramal_meta) + 1)^(1/lambda_meta)
ramal_msft <- 1/(ramal_msft)
ramal_nflx <- (ramal_nflx)
ramal_nvda <- 1/(ramal_nvda)^2

ramal <- data.frame(
  Date = data2 %>% dplyr::select(Date)  ,
  `Adj Close` = c(as.vector(train_aapl.ts), ramal_aapl[-1],
                  as.vector(train_amzn.ts), ramal_amzn[-1],
                  as.vector(train_goog.ts), ramal_goog[-1],
                  as.vector(train_meta.ts), ramal_meta[-1],
                  as.vector(train_msft.ts), ramal_msft[-1],
                  as.vector(train_nflx.ts), ramal_nflx[-1],
                  as.vector(train_nvda.ts), ramal_nvda[-1] ),
  Name = rep(c("AAPL", "AMZN", "GOOG", "META", "MSFT", "NFLX", "NVDA"), 
              each = nrow(train_aapl) + nrow(test_aapl) )
)
colnames(ramal) <- c("Date", "Adj Close", "Name")
chart <- 
ggplot(ramal, aes(x = Date, y = `Adj Close`, color = Name)) +
  #Label Data Asli 
  annotate( "rect", alpha=0.1, fill="seagreen",
            xmin=as.Date(min(data2$Date)), 
            xmax=as.Date(data2$Date[nrow(train_goog)]),
            ymin=0, ymax=Inf ) + 
  
  annotate( "text", color="seagreen",
            x = as.Date(data2$Date[ceiling(nrow(train_goog)/2)]), 
            y = max(data2$`Adj Close`) * 0.95, 
    label = "Data Asli", size=10) + 
  
  #Label Data Ramal
  annotate( "rect", alpha=0.1, fill="violetred",
            xmin=as.Date(data2$Date[nrow(train_meta)]), 
            xmax=as.Date(max(data2$Date)),
            ymin=0, ymax=Inf ) + 
  
  annotate( "text", color="violetred",
            x = as.Date(data2$Date[ceiling(0.925 * length(data2$Date)/7)]) , 
            y = max(data2$`Adj Close`) * 0.95, 
    label = "Data Ramal", size=10) +
  #Time Series
  geom_line(aes(color=Name), linewidth=1.5) +
#AAPL
  geom_point(data = tail(train_aapl, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 15, shape = 21, color = "black", fill="violetred") +
#AMZN
  geom_point(data = tail(train_amzn, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 15, shape = 21, color = "black", fill="violetred") +
#GOOG
  geom_point(data = tail(train_goog, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 15, shape = 21, color = "black", fill="violetred") +
#META
  geom_point(data = tail(train_meta, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 15, shape = 21, color = "black", fill="violetred") +
#MSFT
  geom_point(data = tail(train_msft, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 15, shape = 21, color = "black", fill="violetred") +
#NFLX
  geom_point(data = tail(train_nflx, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 15, shape = 21, color = "black", fill="violetred") +
#NVDA
  geom_point(data = tail(train_nvda, 1), alpha = .5, 
             aes(x = Date, y = `Adj Close`), stroke=2,
             size = 15, shape = 21, color = "black", fill="violetred") +
  #Warna Saham
  scale_color_manual(values = c(NVDA="green4", NFLX="firebrick3", 
                                MSFT="deepskyblue2", META="dodgerblue3", 
                                AAPL="lightskyblue4",
                                GOOG="gold3", AMZN="orange2") ) +
  theme.ts + #THeme
  labs(x = "\nPeriode (Tahun)", y='Harga Saham (USD)',
       title = "Ramalan Saham MAANG",
       subtitle = "Peramalan Saham MAANG Selama 115 Hari Kedepan\n") +
  # Label / legend
  geom_text_repel(
    data=ramal[ramal$Date == max(ramal$Date),], #Posisi di ujung data
    aes(color = Name, label = Name), #Warna garis & label saham
    size = 8, #Ukuran text
    nudge_x = 20, #Posisi Text (kanan 50)
    hjust = 0, #Ujung
    segment.size = 1,               #Ukuran garis
    segment.alpha = .75,             #transparasi garis
    segment.linetype = "dotted",    #Time garis
    box.padding = .4, #Biar label saham nggak dempetan
    segment.curvature = -0.1, #biar garis mulus
    segment.ncp = 8, 
    segment.angle = 60 
  ) +
  #Axis
    coord_cartesian(clip = "off"
  ) +
    scale_x_date( #Sumbu x
    date_breaks = "1 year",  # Menampilkan label setiap tahun
    date_labels = "%Y",  # Format label tahun
    limits = c(as.Date(min(ramal$Date)), 
               as.Date(max(ramal$Date)) + 60)
  ) +
    scale_y_continuous( #Sumbu y
    labels = scales::dollar_format(prefix = "$") #tambahin dolar
  ) +
    annotate( #Buat nandain batas data
    "text", x = as.Date(max(data2$Date)), y = 50, 
    label = max(data2$Date), size=6
  ) +
  geom_vline( #Buat garis batas data
    xintercept = as.numeric(as.Date( max(data2$Date) )), 
             linetype = "dotted", color = "red") 
chart

#Export Chart
ggsave("09_banding_ramal.png", chart, path = export.chart,
        dpi = 300, height = 12, width = 27)

Dapat dilihat bahwa rata-rata harga saham Amazon diramalkan akan cenderung sedikit menurun setiap periodenya. Di sisi lain, saham NVIDIA diramalkan akan menaik pesat setiap periodenya. Sedangkan harga saham lainnya cenderung naik sedikit.

7.2 Perbandingan Data Aktual dengan Data Ramal

7.2.1 AAPL

perbandingan_aapl.da <- matrix(data=c(head(test_aapl.ts, n=nrow(test_aapl)), 
                                      ramal_aapl[-1]),
                     nrow = nrow(test_aapl), ncol = 2)
colnames(perbandingan_aapl.da) <- c("Aktual","Ramal")
datatable(perbandingan_aapl.da, filter = 'top', 
          options = list(pageLength = 5))

7.2.2 AMZN

perbandingan_amzn.da <- matrix(data=c(head(test_amzn.ts, n=nrow(test_amzn)), 
                                      ramal_amzn[-1]),
                     nrow = nrow(test_amzn), ncol = 2)
colnames(perbandingan_amzn.da) <- c("Aktual","Ramal")
datatable(perbandingan_amzn.da, filter = 'top', 
          options = list(pageLength = 5))

7.2.3 GOOG

perbandingan_goog.da <- matrix(data=c(head(test_goog.ts, n=nrow(test_goog)), 
                                      ramal_goog[-1]),
                     nrow = nrow(test_goog), ncol = 2)
colnames(perbandingan_goog.da) <- c("Aktual","Ramal")
datatable(perbandingan_goog.da, filter = 'top', 
          options = list(pageLength = 5))

7.2.4 META

perbandingan_meta.da <- matrix(data=c(head(test_meta.ts, n=nrow(test_meta)), 
                                      ramal_meta[-1]),
                     nrow = nrow(test_meta), ncol = 2)
colnames(perbandingan_meta.da) <- c("Aktual","Ramal")
datatable(perbandingan_meta.da, filter = 'top', 
          options = list(pageLength = 5))

7.2.5 MSFT

perbandingan_msft.da <- matrix(data=c(head(test_msft.ts, n=nrow(test_msft)), 
                                      ramal_msft[-1]),
                     nrow = nrow(test_msft), ncol = 2)
colnames(perbandingan_msft.da) <- c("Aktual","Ramal")
datatable(perbandingan_msft.da, filter = 'top', 
          options = list(pageLength = 5))

7.2.6 NFLX

perbandingan_nflx.da <- matrix(data=c(head(test_nflx.ts, n=nrow(test_nflx)), 
                                      ramal_nflx[-1]),
                     nrow = nrow(test_nflx), ncol = 2)
colnames(perbandingan_nflx.da) <- c("Aktual","Ramal")
datatable(perbandingan_nflx.da, filter = 'top', 
          options = list(pageLength = 5))

7.2.7 NVDA

perbandingan_nvda.da <- matrix(data=c(head(test_nvda.ts, n=nrow(test_nvda)), 
                                      ramal_nvda[-1]),
                               nrow = nrow(test_nvda), ncol = 2)
colnames(perbandingan_nvda.da) <- c("Aktual","Hasil Forecast")
datatable(perbandingan_nvda.da, filter = 'top', 
          options = list(pageLength = 5))

7.3 Akurasi

akurasi.aapl <- accuracy(ts(ramal_aapl[-1]), 
                         head(test_aapl.ts, n=nrow(test_aapl) )) %>%
  as.data.frame() %>%
  cbind(Model = ramalan_aapl.da[["method"]]) %>% 
  relocate(Model, .before = 1)
row.names(akurasi.aapl) <- "AAPL"

akurasi.amzn <- accuracy(ts(ramal_amzn[-1]), 
                         head(test_amzn.ts, n=nrow(test_amzn) )) %>%
  as.data.frame() %>%
  cbind(Model = ramalan_amzn.da[["method"]]) %>% 
  relocate(Model, .before = 1)
row.names(akurasi.amzn) <- "AMZN"

akurasi.goog <- accuracy(ts(ramal_goog[-1]), 
                         head(test_goog.ts, n=nrow(test_goog) )) %>%
  as.data.frame() %>%
  cbind(Model = ramalan_goog.da[["method"]]) %>% 
  relocate(Model, .before = 1)
row.names(akurasi.goog) <- "GOOG"

akurasi.meta <- accuracy(ts(ramal_meta[-1]), 
                         head(test_meta.ts, n=nrow(test_meta) )) %>%
  as.data.frame() %>%
  cbind(Model = ramalan_meta.da[["method"]]) %>% 
  relocate(Model, .before = 1)
row.names(akurasi.meta) <- "META"

akurasi.msft <- accuracy(ts(ramal_msft[-1]), 
                         head(test_msft.ts, n=nrow(test_msft) )) %>%
  as.data.frame() %>%
  cbind(Model = ramalan_msft.da[["method"]]) %>% 
  relocate(Model, .before = 1)
row.names(akurasi.msft) <- "MSFT"

akurasi.nflx <- accuracy(ts(ramal_nflx[-1]), 
                         head(test_nflx.ts, n=nrow(test_nflx) )) %>%
  as.data.frame() %>%
  cbind(Model = ramalan_nflx.da[["method"]]) %>% 
  relocate(Model, .before = 1)
row.names(akurasi.nflx) <- "NFLX"

akurasi.nvda <- accuracy(ts(ramal_nvda[-1]), 
                         head(test_nvda.ts, n=nrow(test_nvda) )) %>%
  as.data.frame() %>%
  cbind(Model = ramalan_nvda.da[["method"]]) %>% 
  relocate(Model, .before = 1)
row.names(akurasi.nvda) <- "NVDA"
akurasi <- rbind(akurasi.aapl, akurasi.amzn, akurasi.goog, akurasi.meta,
                 akurasi.msft, akurasi.nflx, akurasi.nvda) %>%
  mutate(Keterangan = case_when(
    MAPE < 10 ~ "Sangat Baik",
    MAPE >= 10 & MAPE <= 20 ~ "Baik",
    MAPE > 20 & MAPE <= 50 ~ "Layak",
    MAPE > 50 ~ "Tidak Akurat"
  )) %>% relocate(Keterangan, Model, MAPE)

datatable(akurasi, filter = 'top', 
          options = list(pageLength = 7))

Dari tabel diatas terlihat bahwa model ARIMA(0,1,1) Merupakan model yang paling sering digunakan. Model dengan kategori “Sangat baik” hanya ada di GOOG (ARIMA(4,1,5)) dan MSFT (ARIMA(4,1,5)). Di sisi lain, model dengan katergori “Baik” ada pada NFLX (ARIMA(6,1,7)) dan NVDA (ARIMA(0,1,1)). Sisanya mendapatkan kategori “Layak”.

8 Penutup

#Export Data
install_load('openxlsx')
#Model Tentatif 
write.xlsx(list("AAPL" = model.tentaif_aapl, 
                "AMZN" = model.tentaif_amzn, 
                "GOOG" = model.tentaif_goog, 
                "META" = model.tentaif_meta, 
                "MSFT" = model.tentaif_msft, 
                "NFLX" = model.tentaif_nflx, 
                "NVDA" = model.tentaif_nvda), 
           file = "Model_Tentatif.xlsx")

# 1. Model Tanpa Intercept dan Tanpa Drift
write.xlsx(list("AAPL" = drift1_aapl, 
                "AMZN" = drift1_aapl, 
                "GOOG" = drift1_aapl, 
                "META" = drift1_aapl, 
                "MSFT" = drift1_aapl, 
                "NFLX" = drift1_aapl, 
                "NVDA" = drift1_aapl), 
           file = "Model_Drift1.xlsx")

# 2. Model Dengan Intercept Tanpa Drift 
write.xlsx(list("AAPL" = drift2_aapl, 
                "AMZN" = drift2_aapl, 
                "GOOG" = drift2_aapl, 
                "META" = drift2_aapl, 
                "MSFT" = drift2_aapl, 
                "NFLX" = drift2_aapl, 
                "NVDA" = drift2_aapl), 
           file = "Model_Drift2.xlsx")

# 3. Model Tanpa Intercept Dengan Drift
write.xlsx(list("AAPL" = drift3_aapl, 
                "AMZN" = drift3_aapl, 
                "GOOG" = drift3_aapl, 
                "META" = drift3_aapl, 
                "MSFT" = drift3_aapl, 
                "NFLX" = drift3_aapl, 
                "NVDA" = drift3_aapl), 
           file = "Model_Drift3.xlsx")

# 4. Model dengan Intercept dan dengan Drift
write.xlsx(list("AAPL" = drift4_aapl, 
                "AMZN" = drift4_aapl, 
                "GOOG" = drift4_aapl, 
                "META" = drift4_aapl, 
                "MSFT" = drift4_aapl, 
                "NFLX" = drift4_aapl, 
                "NVDA" = drift4_aapl), 
           file = "Model_Drift4.xlsx")